Mybatis缓存、及集成spring
 
简介
Mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解

一级缓存: 是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存: 是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存
示例:
mybatis的config配置
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <configuration>
 <settings>
 <setting name="mapUnderscoreToCamelCase" value="true"/>
 </settings>
 
 <plugins>
 <plugin interceptor="com.tuling.mybatis.interceptor.PageInterceptor">
 <property name="databaseType" value="Mysql" />
 </plugin>
 
 <plugin interceptor="com.tuling.mybatis.interceptor.LogInterceptor">
 </plugin>
 </plugins>
 
 <environments default="development">
 <environment id="development">
 <transactionManager type="JDBC" />
 <dataSource type="POOLED">
 <property name="driver" value="com.mysql.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://47.96.118.93:3306/tuling"/>
 <property name="username" value="root"/>
 <property name="password" value="chen1208"/>
 </dataSource>
 </environment>
 </environments>
 
 <mappers>
 <mapper resource="com/tuling/mybatis/dao/UserMapper.xml"></mapper>
 </mappers>
 
 </configuration>
 
 | 
测试:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | @Testpublic void test() throws IOException {
 String resource = "qingsong-mybatis.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 SqlSession session = sqlSessionFactory.openSession();
 UserMapper userMapper = session.getMapper(UserMapper.class);
 User result1 = userMapper.selectByid(1);
 User result2 = userMapper.selectByid(1);
 System.out.println(result1 == result2);
 }
 
 | 
使用条件
1,必须是相同的SQL和参数
2,必须是相同的会话
3,必须是相同的namespace 即同一个mapper
4,必须是相同的statement 即同一个mapper 接口中的同一个方法
5,查询语句中间没有执行session.clearCache() 方法
6,查询语句中间没有执行 insert update delete 方法(无论变动记录是否与 缓存数据有无关系)
源码解析链路
缓存获取 :
| 12
 3
 4
 5
 
 | >mapper.mapper.selectById(23)>org.apache.ibatis.session.defaults.DefaultSqlSession
 >org.apache.ibatis.executor.CachingExecutor
 >org.apache.ibatis.executor.BaseExecutor
 >org.apache.ibatis.cache.impl.PerpetualCache#getObject
 
 | 
缓存的存储:
| 12
 3
 4
 5
 6
 
 | >mapper.mapper.selectById(23)>org.apache.ibatis.session.defaults.DefaultSqlSession
 >org.apache.ibatis.executor.CachingExecutor
 >org.apache.ibatis.executor.BaseExecutor
 >org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
 >org.apache.ibatis.cache.impl.PerpetualCache#putObject
 
 | 
二级缓存
示例
| 12
 3
 4
 5
 6
 
 | @CacheNamespace()public interface LabelMapper {
 
 @Select("select * from t_label where id =#{id}")
 Label getById(Integer id);
 }
 
 | 
CacheNamespace注解说明:
| 12
 3
 4
 5
 6
 7
 8
 
 | @CacheNamespace(implementation = PerpetualCache.**class**, //  缓存实现 Cache接口 实现类
 eviction = LruCache.**class**,// 缓存算法
 flushInterval = 60000, // 刷新间隔时间 毫秒
 size = 1024,   // 最大缓存引用对象
 readWrite = **true**, // 是否可写
 blocking = false  // 是否阻塞
 )
 
 | 
使用条件
1,当会话提交或关闭之后才会填充二级缓存
2,必须是在同一个命名空间之下
3,必须是相同的statement 即同一个mapper 接口中的同一个方法
4,必须是相同的SQL语句和参数
5,如果readWrite=true ,实体对像必须实现Serializable 接口
清除条件
1,xml中配置的update 不能清空 @CacheNamespace 中的缓存数据
2,只有修改会话提交之后 才会执行清空操作
3,任何一种增删改操作 都会清空整个namespace 中的缓存
源码解析链路
清除缓存
| 12
 3
 4
 
 | >org.apache.ibatis.session.defaults.DefaultSqlSession#selectList() >org.apache.ibatis.executor.CachingExecutor#query()
 >org.apache.ibatis.executor.CachingExecutor#query()
 >org.apache.ibatis.executor.CachingExecutor#flushCacheIfRequired()
 
 | 
获取缓存
| 12
 3
 4
 5
 6
 7
 8
 
 | >org.apache.ibatis.cache.TransactionalCacheManager#getObject >org.apache.ibatis.cache.decorators.TransactionalCache#getObject
 >org.apache.ibatis.cache.decorators.SynchronizedCache#getObject
 >org.apache.ibatis.cache.decorators.LoggingCache#getObject
 >org.apache.ibatis.cache.decorators.SerializedCache#getObject
 >org.apache.ibatis.cache.decorators.ScheduledCache#getObject
 >org.apache.ibatis.cache.decorators.LruCache#getObject
 >org.apache.ibatis.cache.impl.PerpetualCache#getObject
 
 | 
保存二级缓存
| 12
 3
 4
 5
 6
 7
 8
 9
 
 |  >org.apache.ibatis.executor.CachingExecutor#close>org.apache.ibatis.cache.TransactionalCacheManager#commit
 >org.apache.ibatis.cache.decorators.TransactionalCache#flushPendingEntries
 >org.apache.ibatis.cache.decorators.SynchronizedCache#putObject
 >org.apache.ibatis.cache.decorators.LoggingCache#putObject
 >org.apache.ibatis.cache.decorators.SerializedCache#putObject
 >org.apache.ibatis.cache.decorators.ScheduledCache#putObject
 >org.apache.ibatis.cache.decorators.LruCache#putObject
 >org.apache.ibatis.cache.impl.PerpetualCache#putObject
 
 | 
spring 集成myBatis
基本使用
1、配置 SqlSessionFactoryBean
| 12
 3
 
 | <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/>
 </bean>
 
 | 
2、配置 MapperFactoryBean
| 12
 3
 4
 
 | <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="com.tuling.mybatis.dao.UserMapper"/>
 <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
 </bean>
 
 | 
 3、获取mapper 对像执行业务方法
| 12
 3
 
 | context = new ClassPathXmlApplicationContext("spring.xml");UserMapper mapper = context.getBean(UserMapper.class);
 System.out.println(mapper.selectByid(1));
 
 | 
对像说明:
FactoryBean:工厂Bean 用于 自定义生成Bean对像,当在ioc 中配置FactoryBean 的实例时,最终通过bean id 对应的是FactoryBean.getObject()实例,而非FactoryBean 实例本身
SqlSessionFactoryBean:生成SqlSessionFactory 实例,该为单例对像,作用于整个应用生命周期。常用属性如下:
- dataSource:   数据源(必填)
- configLocation:指定mybatis-config.xml 的内容,但其设置的   将会失效(选填)
- mapperLocations:指定mapper.xml 的路径,相当于mybatis-config.xml 中 元素配置,(选填)
MapperFactoryBean:生成对应的Mapper对像,通常为单例,作用于整个应用生命周期。常用属性如下:
- mapperInterface:mapper 接口      (必填)
- sqlSessionFactory:会话工厂实例 引用 (必填)
核心流程解析
// 创建 会话模板 SqlSessionTemplate
| 12
 3
 4
 5
 6
 7
 8
 
 | >com.tuling.mybatis.dao.UserMapper>org.apache.ibatis.binding.MapperProxy
 >org.mybatis.spring.SqlSessionTemplate
 >org.mybatis.spring.SqlSessionTemplate#sqlSessionProxy#selectOne(java.lang.String)
 >org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
 >org.mybatis.spring.SqlSessionUtils#getSqlSession()
 >org.apache.ibatis.session.SqlSessionFactory#openSession()
 >org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne()
 
 | 
事物使用
spring 事物没有针对myBatis的配置,都是一些常规事物配置:
| 12
 3
 4
 5
 6
 
 | <!--添加事物配置--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <constructor-arg ref="dataSource"/>
 </bean>
 <!--事物注解配置-->
 <tx:annotation-driven/>
 
 | 
添加事物注解:
| 12
 3
 4
 5
 
 | @Transactional()public User getUser2(Integer id) {
 userMapper.selectByid(id);
 return userMapper.selectByid(id);
 }
 
 | 
执行测试发现 当调用getUser2 方法时两次查询不在重复创建 sqlSession。而是共用一个直到getUser2 方法结束。
**事物与SqlSession 集成原理: **
其原理前面讲查询流程时有所涉及。每次执行SQL操作前都会通过 getSqlSession 来获取会话。其主要逻辑是 如果当前线程存在事物,并且存在相关会话,就从ThreadLocal中取出 。如果没就从创建一个 SqlSession 并存储到ThreadLocal 当中,共下次查询使用。
相关源码:
| 12
 3
 4
 5
 6
 7
 
 | org.mybatis.spring.SqlSessionUtils#getSqlSession()org.springframework.transaction.support.TransactionSynchronizationManager#getResource
 org.mybatis.spring.SqlSessionUtils#sessionHolder
 org.apache.ibatis.session.SqlSessionFactory#openSession()
 org.mybatis.spring.SqlSessionUtils#registerSessionHolder
 org.springframework.transaction.support.TransactionSynchronizationManager#isSynchronizationActive
 org.springframework.transaction.support.TransactionSynchronizationManager#bindResource
 
 | 
简化Mapper 配置
如果每个mapper 接口都配置*MapperFactoryBean *相当麻烦 可以通过 如下配置进行自动扫描
| 1
 |  <mybatis:scan base-package="com.tuling.mybatis.dao"/>
 | 
其与 spring bean 注解扫描机制类似,所以得加上注解扫描开关的配置
| 1
 | <context:annotation-config/>
 |