给自己的每日一句
不从恶人的计谋,不站罪人的道路,不坐亵慢人的座位,惟喜爱耶和华的律法,昼夜思想,这人便为有福!他要像一棵树栽在溪水旁,按时候结果子,叶子也不枯干。凡他所做的尽都顺利
本文内容整理自《孙哥说Mybatis系列课程》
前言
一:Mapper.xml被读取发生的事
1:Mapper.xml文件中一个标签会对应一个MappedStatement对象,那么没一个查询Select标签也是对应一个MappedStatement
2:Cache对象是基于Mapper.xml文件中的Cache标签创建。一个Cache标签只能创建出来一个Cache对象
结论就是一个Cache对象会被多个MS对象引用,属于一对多的关系。
细节分析
一:缓存内部就是PerpetualCache
PerpetualCache本质上就是个Hashmap,上边两个Select标签标签查询出来的数据都会缓存到这个Map缓存的数据当中。
二:缓存key
Cache中的key被封装成了CacheKey这样的一个对象。
1:key的构成
那什么玩意作为key呢?value这个玩意就存在于List当红在哪个就好了,那么key呢?
key的构成规则大概就是:integer + namespace.id + SQL + 参数 大体上是这么个玩意构成
2:真实key举例
-976698875:6380909722:com.baizhiedu.dao.UserDAO.queryAllUsers:0:2147483647:select id,name from t_user:default
通过这里我们可以知道,基于CacheKey也是能够实现拿到拼接SQL的一种方式,有了对应的SQL和参数,我们可以手动拼接SQL
3:什么时候入缓存?
按照上述的两个SQL语句执行完毕,并且事务提交之后,HashMap当中会有两条缓存数据
4:资源占用问题
一个Select标签对应的MP对象都会存储Cache,这样不是很占用内存资源么?
只是引用,Cache对象只有一个。
证明MappedStatement和Cache对象的多对一关系
一个十分重要的细节:必须事务提交之后,才能把Cache放入到缓存当中。
@Test
public void test2() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
UserDAO userDAO1 = sqlSession1.getMapper(UserDAO.class);
UserDAO userDAO2 = sqlSession2.getMapper(UserDAO.class);
UserDAO userDAO3 = sqlSession3.getMapper(UserDAO.class);
/* List<User> users1 = userDAO1.queryAllUsersByPage();//--- ms ---> cache
for (User user : users1) {
System.out.println("user = " + user);
}*/
User user1 = userDAO1.queryUserById(4); //---ms ---> cache
sqlSession1.commit();
//userDAO1.queryAllUsers();
System.out.println("-----------------------------------------");
/*List<User> users2 = userDAO2.queryAllUsersByPage();
for (User user : users2) {
System.out.println("user = " + user);
}*/
sqlSession2.commit();
System.out.println("-----------------------------------------");
//
/* User user = new User(4, "xiaowb");
userDAO3.update(user);
sqlSession3.commit();*/
}
重点关注这行代码
User user1 = userDAO1.queryUserById(4); //---ms ---> cache
sqlSession1.commit();
sqlSession1.commit();执行后才会放到缓存当中。
在这个位置上边的代码走过ms.getCache的时候,Cache对象size是0;
通过debug来证明的,不同的MappedStatement对应的Cache对象是一个。
自问自答
一:不同的Mapper文件查询标签缓存数据能不能放到一个Cache当中呢?
可以
这样做的目的就是:引用别的Mapper文件中的Cache来存储数据。
二:使用缓存后脏数据怎么办?
所谓脏数据就是对UseDao做更新操作,Mybatis对缓存数据是怎么处理的?
更新操作包括,update,delete,insert,Mybatis会将Mapper对应Cache对象当中所有的key以及对应的内容都清空!也就是Mapper所有缓存数据都干掉!
clear()操作根源是发生在TransactionalCache当中,这Cache的一个装饰器。他的作用就是事务执行完毕之后,完成一些操作,事务完成之后才会写入内存
如果是增删改操作,一定是事务提交的时候,会走将事务清空
public void commit() {
if (clearOnCommit) {
delegate.clear();//删除操作
}
flushPendingEntries();//方缓存操作
reset();//中间对象清楚操作。
}
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
这个TransactionalCache的装饰器是非常好的,只要有增删改,Mapper对应的Cache数据全部被删除,避免脏数据。
关联查询怎么避免缓存脏数据?
关联查询怎么玩呢?也是往缓存中楞塞,但是有这样的一个问题,我们在UserDao.XML当中使用缓存,这样更新User数据会清空Cache。
但是如果有User和Order的关联查询,那么情空Order需要在使用Cache-ref标签的情况下才会出现,更新order数据会刷新User当中Cache标签的问题,因为ref标签会导致两个Mapper.xml用的是一个Cache对象。