MyBatis前身是iBatis,是 Apache 软件基金会下的一个开源项目。2010年该项目从 Apache 基金会迁出,并改名为 MyBatis。
由于现在SpringBoot工程盛行,下面用SpringBoot举个简单的例子:
新建一个maven工程,pom.xml引入MyBatis、MySQL和test依赖:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.7.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
启动类Application.Java,要加上@MapperScan扫描mapper接口:
@SpringBootApplication
@MapperScan("com.**.mapper*")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
配置文件application.yml,配置数据库和MyBatis的SQL映射文件位置:
spring:
datasource:
url: jdbc:mysql://localhost/springbootest?characterEncoding=UTF-8&useUnicode=true&useSSL=false&autoReconnect=true&serverTimezone=UCT
username: root
password: root
driverClassName: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapping/*Mapping.xml
User.java,实体:
public class User {
private Integer id;
private String name;
private String sex;
private Integer age;
private String des;
//加上getter、setter和toString
}
UserService.java
public interface UserService {
List<User> findAll();
}
UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
public List<User> findAll(){
return userMapper.findAll();
}
}
UserMapper.java
@Mapper
public interface UserMapper {
List<User> findAll();
}
UserMapping.xml,写一个查询映射
<mapper namespace="com.mybatis.mapper.UserMapper">
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user;
select>
mapper>
TestMyBatis.java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestMyBatis {
@Autowired
SqlSessionFactory sqlSessionFactory;
@Test
public void test01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findAll();
for (User user : list) {
System.out.println(user.toString());
}
}
}
建表语句和插入语句
DROP TABLE IF EXISTS `test_user`;
CREATE TABLE `test_user` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`NAME` varchar(40) DEFAULT NULL,
`sex` varchar(2) DEFAULT NULL,
`age` int(3) DEFAULT NULL,
`des` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `test_user` VALUES (1,'张三','男',20,'程序员'),(2,'李四','女',18,'程序员');
上面测试代码中注入SqlSessionFactory工厂,该工厂的用途是创建SqlSession。用SqlSession为数据库访问接口UserMapper创建一个代理对象。MyBatis会把接口方法findAll和sql映射文件中配置的SQL关联起来,这样调用该方法等于执行相关的SQL。
运行测试代码,结果如下:
User{id=1, name='张三', sex=男, age=20, des='程序员'}
User{id=2, name='李四', sex=女, age=18, des='程序员'}
(1)接口层:负责增删改查的接口
(2)数据处理层:
参数映射(ParameterHandler):参数映射配置、参数映射解析和参数类型解析。
SQL解析(SQLSource):SQL获取、SQL解析和动态SQL。
配置加载(Executor):SimpleExecutor、BatchExecutor和ResultExecutor。
结果映射(ResultSetHandler):结果映射配置、结果类型转换和结果数据拷贝。
(3)基础支撑层:连接管理、事务管理、配置加载和缓存处理。
insert、update和delete标签常用参数
参数 | 作用 | 备注 |
---|---|---|
id | 命名空间中的唯一标识 | 一般和接口名同名 |
parameterType | 传入参数的全限定类名或别名 | 一般不用写,MyBatis会自动推断出参数类型,默认unset |
flushCache | 任何修改语句被调用二级缓存都会被清空 | 默认值true |
timeout | 等待数据库返回请求结果的秒数 | 默认unset依赖驱动,超时返回异常 |
select标签常用参数
参数 | 作用 | 备注 |
---|---|---|
parameterType | 传入参数的全限定类名或别名 | 一般不用写,MyBatis会自动推断出参数类型,默认unset |
resultType | 返回值类型,全限定类名或别名 | 如果返回集合应该是集合可以包含的类型,而不是集合本身;不能和resultMap同时使用 |
resultMap | 外部resultMap的命名引用 | 不能和resultType同时使用 |
useCache | 本条语句结果会被二级缓存 | select默认值true |
timeout | 等待数据库返回请求结果的秒数 | 默认unset依赖驱动,超时返回异常 |
这里写一个插入语句:
//UserService.java
public interface UserService {
List<User> findAll();
int addUser(User user);
}
//UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
public List<User> findAll(){
return userMapper.findAll();
}
@Override
public int addUser(User user) {
return userMapper.addUser(user);
}
}
//UserMapper.java
@Mapper
public interface UserMapper {
List<User> findAll();
int addUser(@Param("user") User user);
}
//UserMapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.UserMapper">
<insert id="addUser">
insert into test_user (name,sex,age,des) values (#{user.name},#{user.sex},#{user.age},#{user.des})
</insert>
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user
</select>
</mapper>
//TestMyBatis.java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestMyBatis {
@Autowired
SqlSessionFactory sqlSessionFactory;
@Test
public void test01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("王五");
user.setAge(10);
user.setDes("农民工");
user.setSex("男");
userMapper.addUser(user);
List<User> list = userMapper.findAll();
for (User u : list) {
System.out.println(u.toString());
}
}
}
执行测试方法打印如下:
User{id=1, name='张三', sex=男, age=20, des='程序员'}
User{id=2, name='李四', sex=女, age=18, des='程序员'}
User{id=3, name='王五', sex=男, age=10, des='农民工'}
MyBatis的强大特性之一就是它的动态SQL,动态SQL就是对SQL语句进行灵活操作,通过表达式进行判断,对SQL进行灵活的拼装或者组装。
<if test="boolean表达式"></if>
例如用名字查询的时候,这里只演示映射文件写法:
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user
<if test="name!=null and name !=''">
where name = #{name}
</if>
</select>
但是要注意传入参数的时候要使用@Param
List<User> findAll(@Param("name") String name);
类似于Java中的switch判断
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user where 1 = 1
<choose>
<when test="name!=null and name!=''">
and name = #{name}
</when>
<otherwise>
and sex = #{sex}
</otherwise>
</choose>
</select>
如果where标签中的查询语句开头有一个and或者or,会把第一个and或者or替换成where,这样就避免了where 1=1的形式;如果没有就直接加上where。
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user
<where>
<if test="name != null and name != ''">
and name = #{name}
</if>
</where>
</select>
set标签和where功能类似,也能根据sql动态去掉最后的逗号,并在前面加上set关键字。
<update id="update">
update test_user
<set>
<if test="user.name != null and user.name != ''">
name = #{user.name},
</if>
<if test="user.sex != null and user.sex != ''">
sex = #{user.sex},
</if>
</set>
where id = #{user.id}
</update>
trim的功能更强大:
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id=#{id} and
</if>
<if test="name!=null">
name=#{name} and
</if>
</trim>
</select>
用于迭代集合或者数组
例如使用in的时候,ids传入id的数组:
<select id="findAll" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user where id in
<foreach collection="ids" open="(" close=")" separator=",">
</foreach>
</select>
或者批量插入数据时:
<insert id="addUser">
insert into test_user (name,sex,age,des) values
<foreach collection="users" separator="," item="user">
(#{user.name}, #{user.sex}, #{user.age}, #{user.des})
</foreach>
</insert>
使用sql标签可以吧相同的SQL片段提取出来,使用include可以在任意位置中使用,bind标签可以使用OGNL表达式创建一个变量,并将其绑定在上下文中:
<sql id="test_user">
select id,name,sex,age,des from test_user
</sql>
<select id="findAll" resultType="com.mybatis.entity.User">
<include refid="test_user"></include>
<where>
<bind name="nameOrId" value="'%'+ nameOrId +'%'"/>
and (id like #{nameOrId} or name like #{nameOrId})
</where>
</select>
先看代码
//映射文件
<select id="queryByIdAndName" resultType="com.mybatis.entity.User">
select id,name,sex,age,des from test_user where id = #{id} and name = '${name}'
</select>
//测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestMyBatis {
@Autowired
SqlSessionFactory sqlSessionFactory;
@Test
public void test01(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.queryByIdAndName("王五",2);
}
}
开启debug
logging:
level:
com.mybatis.mapper: debug
输出:
: HikariPool-1 - Starting...
: HikariPool-1 - Start completed.
: ==> Preparing: select id,name,sex,age,des from test_user where id = ? and name = '王五'
: ==> Parameters: 2(Integer)
: <== Total: 0
: HikariPool-1 - Shutdown initiated...
: HikariPool-1 - Shutdown completed.
‘%${name}%’
可能引起SQL注入,不推荐。"%"#{name}"%"
在解析#{}时会自动在两侧加上单引号,所有这里不能使用单引号。concat('%',#{name},'%')
使用concat函数,推荐。简单来讲,缓存就是把数据存储在缓冲区里的内容,或者可以理解为存储在内存中的内容。用户可以将经常查询的数据放到缓存中,再次使用时直接从缓存中取值,而不需要再查询数据库。这样做的优点是响应迅速,减少了系统资源(网络资源、CPU资源等)开销;缺点是需要占用内存资源,服务器一旦关机,缓存就会丢失。
作用域是SqlSession级别的,每个SqlSession对象都有一个哈希表用于存放缓存数据,不同的SqlSession缓存数据不共享。同一个SqlSession对象执行两次相同的查询,第一次查询完毕之后会将结果缓存起来,第二次就不会在数据库查询,从缓存中直接获取。如果执行了增删改操作,缓存会被清空。一级缓存默认开启。
作用域是namespace级别的,SqlSession共享,不同的SqlSession对象查询两次相同的语句,第一次会查数据库,第二次会从缓存从获取。如果执行了增删改操作,缓存会被清空。二级缓存默认不开启,可以通过配置文件开启:
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
或者SpringBoot配置开启,这里只演示yaml格式:
mybatis:
configuration:
cache-enabled: true
二级缓存参数:
参数名 | 作用 | 备注 |
---|---|---|
eviction | 清除缓存回收策略LRU、FIFO、SOFT和WEAK | |
flushInterval | 到时间缓存清空 | 单位毫秒 |
readOnly | 只读,只读的缓存会给调用者返回缓存对象相同的实例 | 比较耗性能,默认false |
size | 要记住缓存的对象数目和运行环境的可用内存资源数目 | 默认1024 |
回收策略:
策略 | 作用 | 备注 |
---|---|---|
LRU | 最近最少使用的,最长时间不被使用的 | 默认值 |
FIFO | 先进先出 | |
SOFT | 移除基于垃圾回收器状态和软引用规则的对象 | |
WEAK | 更积极的移除基于垃圾收集器状态和弱引用规则的对象 |