目录
1.Mybatis一级缓存
先说一级缓存和二级缓存的关系及区别:
Mybatis缓存其实也是一种减少数据库IO操作的技术手段,这个缓存存储在本地的一个类似于HashMap当中
其中一级缓存默认是开启的,就是说第一次查询出的结果会被缓存到本地他的作用域是同一个sqlSession,方便同一会话内的查询复用
而二级缓存是跨SqlSession的,开启后可将一级缓存数据复制到二级缓存,提升数据共享性。两种缓存都会在增删改操作后被清除,一级缓存的数据会在SqlSession提交或关闭时转移到二级缓存。
《======================================================================》
看到这张图是不是很熟悉呢,没错缓存!一个我们Java程序员耳熟能详的东西,说白了就是在数据库前面挡住一层不直接去查数据库减少IO。那么我们今天说的关于Mybatis的缓存也是这个作用。
我们先说一级缓存,一级缓存基于本地缓存也称之为PerpetualCache 其实就是个HashMap,其存储作用域为 Session,当Session进行flush(刷新)或close(释放)之后,该Session中的所有缓存就将清空,默认打开一级缓存
我们看代码,我们获取一个sqlSession用它来执行SQL,然后获取UserMapper接口的代理对象UserMapper然后查询ID为6的数据,两次查询结果我来告诉大家,只执行了一次SQL操作只查询了一次数据库,也就是说我们第二次去查询ID为6的数据走了第一次查询的缓存,那么这个缓存的作用域就是同一个sqlSession。
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 执行sql
//3.1 获取UserMapper接口的代理对象UserMapper userMapper1=sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);
User user = userMapper1.selectById(6);
System.out.println(user);
System.out.println("---------------------");
User user1 = userMapper2.selectById(6);
System.out.println(user1);
2.Mybatis二级缓存
二级缓存是基于namespace(命名空间)和mapper的作用域起作用的,不是依赖于SQL session,默认也是采用 PerpetualCache HashMap 存储
我们看代码创建了两个sqlSession,打印结果我告诉大家,查询了两次数据库,也就是局限在了一级缓存作用域Session中
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession1 = sqlSessionFactory.openSession();
//3. 执行sql
//3.1 获取UserMapper接口的代理对象
UserMapper userMapper1 =sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.selectById(6);
System.out.println(user1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
System.out.println("---------------------");
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.selectById(6);
System.out.println(user2);
//4.关闭资源
sqlSession2.close();
现在我们打开二级缓存(默认是关闭的)
开启方式,两步:
1.全局配置文件
<settings>
<setting name="cacheEnabled" value="true
</settings>
2.映射文件
使用<cache/>标签让当前mapper生效二级缓存
这样二级缓存就生效了,在打印时就只查询了一次缓存
二级缓存注意事项
1,对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear(意思就是缓存是需要更新的,保证数据的一致性,那么只要当有新增修改删除操作,缓存就会被删除)
2,二级缓存需要缓存的数据实现Serializable接口
3,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中(意思就是只有第一个sqlSession 进行刷新(flush)或者清除(close))缓存的数据才会同步到二级缓存
3.Mybatis执行流程
①读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件
②构造会话工厂SqlSessionFactory,一个项目只需要一个,单例的,一般由spring进行管理
③会话工厂创建SqlSession对象,这里面就含了执行SQL语句的所有方法
④操作数据库的接口,Executor执行器,同时负责查询缓存的维护
⑤Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
⑥输入参数映射
⑦输出结果映射
4.Mybatis延迟加载
延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据,比如现在有两张表,用户表、订单表,一个用户可以有多个订单也就是一对多关系,体现到延迟加载就是,查询用户的时候,把用户所属的订单数据也查询出来这个是立即加载,查询用户的时候暂时不查询订单数据,当需要订单的时候,再查询订单,这个就是延迟加载。Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载。
具体实现
其实说白了就是在执行一对多SQL时,需要把用户表和对应订单表的数据集合查询出来,那么就先判断一下查询集合的SQL是否为空,如果为空就去查询出来,然后在set进去,最后完成。
1. 使用CGLIB创建目标对象的代理对象
2. 当调用目标方法user.getOrderList()时,进入拦截器invoke方法,发现user.getOrderList()是null值,执行sql查询order列表
3. 把order查询上来,然后调用user.setOrderList(List<Order> orderList) ,接着完成user.getOrderList()方法的调用