1,缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。缓存可以极大的提升查询的效率。
在 MyBatis 中默认定义了两级缓存:一级和二级缓存:
- 在默认情况下,只开启了一级缓存,也就是sqlsession级别的缓存
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
1.1,一级缓存
一级缓存也叫本地缓存,作用域默认为 SqlSession。当 Session flush 或 close 后,该 Session 中的所有 Cache 都将被清除。
一级缓存不能关闭,但可以调用 clearCache() 来清空一级缓存。
1.1.1,编写接口
在EmployeeMapper接口中,添加方法:
Employee findEmployeeById(int id);
1.1.2,配置映射
<resultMap id="rm" type="Employee">
<id property="id" column="id" />
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="did" column="did"/>
</resultMap>
<select id="findEmployeeById" resultMap="rm">
select id,name,age,did from tb_employee where id=#{id}
</select>
1.1.3,测试
@Test
public void testFindEmployeeById() {
SqlSession session = MyBatisUtil.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.findEmployeeById(5);
System.out.println(employee);
// 第二次查询
Employee employee1 = mapper.findEmployeeById(5);
System.out.println(employee1);
session.close();
}
执行后输出:
15:40:08.591 main DEBUG findEmployeeById ==> Preparing: select id,name,age,did from
tb_employee where id=?15:40:08.629 main DEBUG findEmployeeById
> Parameters: 5(Integer)
2022-02-26 15:40:08.648 main DEBUG findEmployeeById
< Total: 1
Employee(id=5, name=刘备, age=20, did=1, depart=null)
Employee(id=5, name=刘备, age=20, did=1, depart=null
从上面的输出看到,查询时只发出一条sql语句,第二次查询时候,并没有再发出sql,而是直接从缓存中获取。当我们在两次查询中调用SqlSession.clearCache() ,会清空一级缓存中的数据:
@Test
public void testFindEmployeeById() {
SqlSession session = MyBatisUtil.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.findEmployeeById(5);
System.out.println(employee);
// 调用清空Cache方法
session.clearCache();
// 第二次查询
Employee employee1 = mapper.findEmployeeById(5);
System.out.println(employee1);
session.close();
这样后输出为:
15:43:45.273 main DEBUG findEmployeeById
> Preparing: select id,name,age,did from tb_employee where id=?
15:43:45.309 main DEBUG findEmployeeById
2022-02-26 15:43:45.323 main DEBUG findEmployeeById
< Total: 1
Employee(id=5, name=刘备, age=20, did=1, depart=null)
15:43:45.337 main DEBUG findEmployeeById
==> Preparing: select id,name,age,did from tb_employee where id=?
15:43:45.338 main DEBUG findEmployeeById
> Parameters: 5(Integer)
2022-02-26 15:43:45.341 main DEBUG findEmployeeById
< Total: 1
Employee(id=5, name=刘备, age=20, did=1, depart=null)
1.1.4,会使一级缓存失效的情况
一级缓存在以下四种情况下会失效:
- 不同的 SqlSession对应不同的一级缓存
- 同一个 SqlSession,但查询条件不同
- 同一个 SqlSession,但是两次查询期间执行了任何一次插入|修改|删除操作
- 同一个 SqlSession,但是两次查询期间手动清空缓存
1.2,二级缓存
二级缓存是Namespace级别的缓存,它是全局缓存。默认情况下,二级缓存没有开启,需要手动配置。
MyBatis 提供了二级缓存的接口以及实现,缓存实现要求我们的 Java 实体对象需要实现 Serializable 接口。二级缓存在 SqlSession 关闭或提交之后才会生效。
使用步骤:
- 在全局配置文件中开启二级缓存
<settings name="cacheEnabled" value="true"/>
- 在需要使用二级缓存的映射文件中使用 cache 标签来配置
<cache/>
- 实体类需要实现 Serializable 接口
1.2.1,开启二级缓存
修改mybatis-config.xml文件,添加:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
1.2.2,添加实体类
@Data
@Accessors(chain = true)
public class Employee implements Serializable {
private Integer id;
private String name;
private int age;
private Integer did;
// 关联部门,它是多对一关系
private Department depart;
}
1.2.3,修改映射文件
<!-- 使用二级缓存 -->
<cache/>
<!-- 根据ID查询 -->
<resultMap id="rm" type="Employee">
<id property="id" column="id" />
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="did" column="did"/>
</resultMap>
<select id="findEmployeeById" resultMap="rm">
select id,name,age,did from tb_employee where id=#{id}
</select>
1.2.4,测试
@Test
public void testFindEmployeeById() {
SqlSession session = MyBatisUtil.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.findEmployeeById(5);
System.out.println(employee);
session.close();
SqlSession session1 = MyBatisUtil.openSession();
EmployeeMapper mapper1 = session1.getMapper(EmployeeMapper.class);
Employee employee1 = mapper1.findEmployeeById(5);
System.out.println(employee1);
session1.close();
}
输出结果:
15:59:35.123 main DEBUG EmployeeMapper Cache Hit Ratio [com.xianopeng.mapper.EmployeeMapper]: 0.0
15:59:35.322 main DEBUG findEmployeeById ==> Preparing: select id,name,age,did from tb_employee where id=?
15:59:35.346 main DEBUG findEmployeeById ==> Parameters: 5(Integer)
15:59:35.364 main DEBUG findEmployeeById <== Total: 1
Employee(id=5, name=刘备, age=20, did=1, depart=null)
15:59:35.396 main WARN SerialFilterChecker As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
15:59:35.401 main DEBUG EmployeeMapper Cache Hit Ratio [com.xianopeng.mapper.EmployeeMapper]: 0.5
Employee(id=5, name=刘备, age=20, did=1, depart=null)
2,注解
MyBatis3开始支持注解
2.1 环境准备
我们以如下表来进行演示:
create table tb_user
(
id int auto_increment primary key,
name varchar(30) null,
account varchar(20) not null,
password varchar(32) not null,
avatar varchar(200) null
);
2.1.1 实体类
User.java
@Data
@Accessors(chain = true)
public class User implements Serializable {
private Integer id;
private String name;
private String account;
private String password;
private String avatar;
}
2.1.2 编写接口
UserMapper.java
public interface UserMapper {
void add(User user);
void update(User user);
void delete(Integer id);
User getById(Integer id);
List<User> getUsers();
List<User> getUserByAccount(String account);
}
2.2 insert
对插入数据,我们使用 @insert 注解来实现
@SelectKey(keyColumn="id", keyProperty="id", before = false, resultType = int.class, statement="select last_insert_id()")
@Insert("insert into tb_user(name,account,password,avatar) values(#{name},#{account},#{password},#{avatar})")
void add(User user);
把接口文件添加到 mybatis-config.xml 文件中:
<mappers>
<mapper resource="DepartmentMapper.xml"/>
<mapper resource="EmployeeMapper.xml"/>
<mapper class="com.xianopeng.mapper.UserMapper"/>
</mappers>
编写测试:
@Test
public void testInsert() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User().setName("王五")
.setAccount("wangwu")
.setPassword("123")
.setAvatar("aa.jpg");
mapper.add(user);
session.commit();
session.close();
System.out.println(user);
}
2.3 update
使用 @Update 注解来实现更新的操作。
@Update("update tb_user set name=#{name},password=#{password} where id=#{id}")
void update(User user);
编写测试:
@Test
public void testUpdate() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User().setName("赵六")
.setPassword("123")
.setId(4);
mapper.update(user);
session.commit();
session.close();
System.out.println(user);
}
2.4 delete
使用 @Delete 注解来实现
@Delete("delete from tb_user where id=#{id}")
void delete(Integer id);
编写测试:
@Test
public void testDelete() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int id = 4;
mapper.delete(id);
session.commit();
session.close();
}
2.5 select
使用 @Select 注解来进行查询功能。
2.5.1 单条查询
@Select("select id,name,account,password,avatar from tb_user where id=#{id}")
User getById(Integer id);
编写测试:
@Test
public void testGetById() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int id = 3;
User user = mapper.getById(id);
System.out.println(user);
}
查询多条数据:
@Select("select id,name,account,password,avatar from tb_user")
List<User> getUsers();
编写测试:
@Test
public void testGetUsers() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
session.close();
users.forEach(System.out::println);
}
我们还可以返回 resultMap 类型。
@Results(id = "userResult", value = {
@Result(property = "id", column = "id", id = true), // 主键
@Result(property = "name", column = "name"),
@Result(property = "account", column = "account"),
@Result(property = "password", column = "password")
})
@Select("select id,name,account,password,avatar from tb_user where account=#{account}")
List<User> getUserByAccount(String account);
编写测试:
@Test
public void testGetUserByAccount() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getUserByAccount("zs");
session.close();
users.forEach(System.out::println);
}
2.6 script
很多时间,我们在做插入、更新、查询时,会有条件,那么我们希望使用 set、where、trim 这些元素,那么就可以通过 script 元素来使用。
例如,我们实现带条件的修改。
@Update({"<script>" +
"update tb_user " +
"<trim prefix='set' suffixOverrides=','>" +
"<if test='name!=null'>name=#{name},</if>" +
"<if test='password!=null'>password=#{password},</if>" +
"<if test='avatar!=null'>avatar=#{avatar},</if>" +
"</trim>" +
"where id=#{id}" +
"</script>"})
void update(User user);
再次执行更新来验证。
@Test
public void testUpdate() {
SqlSession session = MyBatisUtil.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User().setName("王麻子")
.setPassword("123456")
.setId(3);
mapper.update(user);
session.commit();
session.close();
System.out.println(user);
}