我们可以将一些变动不大且访问频率高的数据,放置在一个缓存容器中,用户下一次查询时就从缓存容器中获取结果。
一级查询缓存
一级缓存是基于同一个SqlSession类的实例对象的。不同的SqlSession类的实例对象缓存的数据区域是互不影响的。MyBatis默认支持一级缓存。
一级缓存测试示例
例如,使用同一个sqlSession对象,对id为1的用户查询两次:
@Test
public void testFindCustomerCache1() throws Exception{
SqlSession sqlSession=dataConnection.getSqlSession();
//调用userMapper的方法
User user1=
sqlSession.selectOne("test.findUserById",1);
System.out.println("用户姓名:"+user1.getUsername());
User user2=
sqlSession.selectOne("test.findUserById",1);
System.out.println("用户姓名:"+user2.getUsername());
sqlSession.close();
}
查看运行结果:
从图中可以看出第一次查询id为1的用户信息时,首先打开数据库连接,建立数据库连接,然后预编译SQL语句,设置输入参数,最后从数据库中查询了id为1的用户的信息。而第二次查询id为1的用户时,发现没有任何日志输出,仅仅打印了取出数据时的信息,这说明第二次数据不是从数据库查询出来的,是从一级缓存中获取的。
在第二次查询id为1的用户的信息之前,修改用户的一个属性:
@Test
public void testFindCustomerCache2() throws Exception{
SqlSession sqlSession=dataConnection.getSqlSession();
//调用userMapper的方法
User user1=
sqlSession.selectOne("test.findUserById",1);
System.out.println("用户姓名:"+user1.getUsername());
String username = "章却";
user1.setUsername(username);
System.out.println("修改用户名为:"+username);
sqlSession.update("test" +
".updateUserName",user1);
sqlSession.commit();
User user2=
sqlSession.selectOne("test.findUserById",1);
System.out.println("用户姓名:"+user2.getUsername());
sqlSession.close();
}
测试结果:
从中可以看出在查询缓存数据之前,如果发生了增、删、改等改变数据的操作,SqlSession类都会清空其一级缓存。
二级查询缓存
如果出现无法使用一级缓存的时候,可以使用二级缓存。
二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的SQL语句,多个SqlSession类的实例对象可以共用二级缓存,二级缓存是以namespace为单位的,不同namespace下的操作互不影响。
二级缓存测试实例
首先开启二级缓存需要两步操作,第一步在MyBatis的全局配置文件添加:
<setting name="cacheEnabled" value="true" />
然后要在需要开启二级缓存的具体mapper.xml文件中开启二级缓存:
<cache/>
将要查询的客户信息的Java实体类序列化接口:
package cn.com.mybatis.po;
import java.io.Serializable;
import java.util.List;
//Serializable接口没有方法或字段,仅用于标识可序列化的语义
public class Customer implements Serializable{
private int cus_id;
private String username;
private String acno;
private String gender;
private String phone;
private List<Batch> batchList;
//set和get方法省略
}
Customer类实现序列化接口的原因是,为了将缓存数据取出来执行反序列化操作,因为二级缓存数据存储介质多种多样(如内存、硬盘、服务器),不一定都在内存中。
@Test
public void testFindCustomerOnMapper1() throws Exception{
SqlSession sqlSession =
dataConnection.getSqlSession();
//获取Mapper代理
CustomerMapper customerMapper1 = sqlSession.getMapper(CustomerMapper.class);
//执行Mapper代理对象的查询方法
Customer customer1 =
customerMapper1.findCustomerById(1);
System.out.println("用户姓名:"+customer1.getUsername() + "|" + "卡号:"+customer1.getAcno());
//获取Mapper代理
CustomerMapper customerMapper2 = sqlSession.getMapper(CustomerMapper.class);
//执行Mapper代理对象的查询方法
Customer customer2 =
customerMapper2.findCustomerById(1);
System.out.println("用户姓名:"+customer2.getUsername() + "|" + "卡号:"+customer2.getAcno());
}
运行测试结果:
验证二级缓存清空
当某个Mapper执行了增、删、改、查的操作时,二级缓存会被清空,以防止数据脏读。
@Test
public void testFindCustomerOnMapper2() throws Exception{
SqlSession sqlSession =
dataConnection.getSqlSession();
//获取Mapper代理
CustomerMapper customerMapper1 = sqlSession.getMapper(CustomerMapper.class);
//执行Mapper代理对象的查询方法
Customer customer1 =
customerMapper1.findCustomerById(1);
System.out.println("用户姓名:"+customer1.getUsername() + "|" + "卡号:"+customer1.getAcno());
CustomerMapper customerMapper3 = sqlSession.getMapper(CustomerMapper.class);
String AcNo = "3228286666666";
customer1.setAcno(AcNo);
customerMapper3.UpdateCustomerAcNo(customer1);
System.out.println("修改用户姓名:" + customer1.getUsername() + "|" +"的卡号为:"+customer1.getAcno());
sqlSession.commit();
//获取Mapper代理
CustomerMapper customerMapper2 = sqlSession.getMapper(CustomerMapper.class);
//执行Mapper代理对象的查询方法
Customer customer2 =
customerMapper2.findCustomerById(1);
System.out.println("用户姓名:"+customer2.getUsername() + "|" + "卡号:"+customer2.getAcno());
sqlSession.close();
}
运行测试结果:
需要注意,在使用二级缓存时,需要保证该Mapper下的数据不会在其他Mapper.xml文件中有缓存。比如,在UserMapper.xml中有很多针对user表的操作,但是在一个xxxMapper.xml中,还有针对user单表的操作。这会导致user在两个命名空间下的缓存数据不一致。
如果无法避免这种情况的发生,且又需要使用二级缓存,建议使用拦截器判断执行的SQL涉及哪些表,然后把相关表的缓存清空。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。