MyBatis是一种持久层框架,和Hibernate等一样是作为操作数据库底层的处理方案。持久层的作用可以把实体对象转换为SQL语句,或者使用自定义的SQL语句去操作数据库,然后把返回的数据再映射为实体对象返回给调用者。
在传统的 JDBC 中,我们除了需要自己提供 SQL 外,还必须操作 Connection、Statment、ResultSet,不仅如此,为了访问不同的表,不同字段的数据,我们需要些很多雷同模板化的代码,闲的繁琐又枯燥。
而使用了 MyBatis 之后,只需要提供 SQL 语句就好了,其余的诸如:建立连接、操作 Statment、ResultSet,处理 JDBC 相关异常等等都可以交给 MyBatis 去处理,我们的关注点于是可以就此集中在 SQL 语句上,关注在增删改查这些操作层面上。
创建一个 Maven工程,工程结构目录如下:
1.1.1.2. 配置pom文件将需要的包引入到工程里面,以下是需要的jar包配置。
junit
junit
4.12
test
commons-logging
commons-logging
1.2
test
org.slf4j
log4j-over-slf4j
1.7.21
test
ch.qos.logback
logback-classic
1.2.3
test
org.mybatis
mybatis
3.2.8
org.mybatis
mybatis-spring
1.2.2
org.springframework
spring-test
4.1.3.RELEASE
org.springframework
spring-context
commons-logging
commons-logging
4.1.3.RELEASE
org.springframework
spring-tx
4.1.3.RELEASE
org.springframework
spring-jdbc
4.1.3.RELEASE
mysql
mysql-connector-java
5.1.46
com.alibaba
druid
1.1.12
1.1.1.3. 增加相关的配置文件
如果是开发完整的web工程,需要编写相应的配置文件,我们只是为了简单的使用测试使用一下MyBatis相关的功能,所以只配置这一个相关的即可。
注:1、在配置sqlsessionFactory中关于configLocation是配置MyBatis自己相关的一些配置的,目前使用不到,可以不用配置。
2、配置扫描包路径必须和项目中真实路径保持一致
这里看到引入了一个数据库相关的配置文件,所以编写一个db.properties,编写数据库连接相关的内容
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
还需要一个Mybatis自己相关的配置文件SqlMapConfig,,里面配置了日志和缓存相关的内容。
此时的工程目录结构如下:
接下来编写实体和Mybatis的mapper代理,由于我们只是使用junit进行单元测试的方式进行mybatis的学习,就不编写controller相关的代码了。
1.1.1.4. 编写相关的Java代码在src/java下面新建com.zzlh.mp.entity包,里面存放的是各种实体类对应数据库中的每一张表。我们以user表为例创建一个User.java文件。
public class User {
//对应数据库中的表字段
private Long id;
private String name;
private Integer age;
private String email;
public User() {
}
//方便创建测试用例重写了实例化方法
public User(Long id, String name, Integer age, String email) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
//方便打印输出,重写了toString方法
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + ''' +
", age=" + age +
", email='" + email + ''' +
'}';
}
在src/java下面新建com.zzlh.mp.mapper包,里面存放的是MyBatis的代理接口和对应的xml文件。
首先UserMapper.java,是一个接口类,里面定义了各种接口方法。
package com.zzlh.mp.mapper;
import com.zzlh.mp.entity.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
int insertUser(User user);
int deleteUserById(int userId);
int updateUser(User user);
User getUserById(int userId);
User selectOne(User user);
List listUserByMap(Map paramMap);
List listUser(User user);
List selectAll();
}
然后是UserMapper.xml,里面是根据Mybatis语法规则编写的sql语句。
insert into user (id,name,age,email) value (#{id,jdbcType=DECIMAL},#{name,jdbcType=VARCHAR},#{age,jdbcType=DECIMAL},#{email,jdbcType=VARCHAR})
delete from user where id = #{userId,jdbcType=DECIMAL}
update user set name=#{name,jdbcType=VARCHAR},age=#{age,jdbcType=DECIMAL},email=#{email,jdbcType=VARCHAR} where id=#{id,jdbcType=DECIMAL}
我们可以看到xml文件中每一个标签通过id和java文件的接口方法一一对应。里面就是我们写的SQL语句,然后参数通过MyBatis的表达式来进行标识。这时候的工程目录结构如下:
1.1.1.5. 编写测试类在test/java下面创建com.zzlh.mp包,在里面首先添加一个JUnit4ClassRunner.java,这个类用来加载log4j.properties文件。
public class JUnit4ClassRunner extends SpringJUnit4ClassRunner {
static {
try {
Log4jConfigurer.initLogging("config/log4j.properties");
} catch (FileNotFoundException ex) {
System.err.println("Cannot Initialize log4j");
}
}
public JUnit4ClassRunner(Class> clazz) throws InitializationError {
super(clazz);
}
}
然后编写一个测试类,SampleTest.java来测试功能。
@RunWith(JUnit4ClassRunner.class)//自定义加载过log4j配置文件的类
@ContextConfiguration(locations = {"classpath*:spring.xml"})
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
List userList = userMapper.selectAll();
userList.forEach(System.out::println);
}
}
工程目录如下:
运行测试方法testSelect(),在控制台输出的日志可以看到以下异常:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.zzlh.mp.mapper.UserMapper.selectAll
at org.apache.ibatis.binding.MapperMethod$SqlCommand.(MapperMethod.java:189)
at org.apache.ibatis.binding.MapperMethod.(MapperMethod.java:43)
at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:58)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:51)
at com.sun.proxy.$Proxy14.selectAll(Unknown Source)
at com.zzlh.mp.SampleTest.testSelect(SampleTest.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.frameworkMethod$1.runReflectiveCall(frameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.frameworkMethod.invokeExplosively(frameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
这是编译的时候,src/java下面的*maaper.xml的文件没有被编译,所以我们需要在pom文件里面配置将它编译。
src/main/java
**/*.xml
false
再次运行测试类,查看日志,执行成功,打印出来sql语句和输出结果。
com.zzlh.mp.mapper.UserMapper.selectAll - ==> Preparing: select * from user
com.zzlh.mp.mapper.UserMapper.selectAll - ==> Parameters:
com.zzlh.mp.mapper.UserMapper.selectAll - <== Total: 7
org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2692b61e]
org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
User{id=1, name='Jone', age=18, email='test1@baomidou.com'}
User{id=2, name='Jack', age=20, email='test2@baomidou.com'}
User{id=3, name='Tom', age=28, email='test3@baomidou.com'}
User{id=4, name='Sandy', age=21, email='test4@baomidou.com'}
User{id=5, name='Billie', age=24, email='test5@baomidou.com'}
User{id=6, name='张三', age=34, email='22@qq.com'}
User{id=7, name='李四', age=33, email='ls@qq.com'}
注:1、控制台输出的sql语句是没有经过整合的,就是语句和参数是分离的,如果需要排查问题,需要自己整合sql语句,如果参数过多可能比较麻烦,针对IDEA有MyBatis的插件MyBatis Log Plugin可以将sql自动整合。
2、mapper.java和mapper.xml放在一起不用配置,但是一般把配置类的文件放在java目录里面比较不推荐,一般是将xml文件放在resources里面然后通过spring配置文件指定即可。所以我们可以改为。
首先将pom的build配置取消,然后在spring.xml文件里面sqlSessionFactory配置上mapper.xml的路径。
此时目录结构如下:
再次运行测试类,运行成功。
1、MyBatis这种持久化技术和jdbc、hibernate对比的特点是
- 首先是sql和java代码分离,好处是让sql分离维护,临时修改不需要重新编译。
- 支持定制化 SQL、存储过程以及高级映射。
- 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2、MyBatis的缺点
- mapper.xml里面的sql片段不容易维护。当一个mapper里面的方法越来越多,sql片段就会越凌乱,不方便查找,在IDEA之前的Eclipse等编辑器里面要想找到一个对应的sql片段效率是很低的。
举个例子 cust-mapper.xml - 可定制化的有点是灵活,缺点就是不容易维护和二次开发,比如数据库字段更新了,要重新生成po和sql代码片段,如果是我们对po文件增加过属性,那么重新生成的po文件就不能直接使用要么用对比工具排查,要么使用包装类继承原生po。不管怎么处理都不方便维护和管理。
- 每一个对应的mapper都要写大量的基础增删改查的方法和语句。



