Mybatis缓存、及集成spring
简介
Mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解
一级缓存: 是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存: 是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存
示例:
mybatis的config配置
1 2 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>
|
测试:
1 2 3 4 5 6 7 8 9 10 11
| @Test public 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 方法(无论变动记录是否与 缓存数据有无关系)
源码解析链路
缓存获取 :
1 2 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
|
缓存的存储:
1 2 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
|
二级缓存
示例
1 2 3 4 5 6
| @CacheNamespace() public interface LabelMapper { @Select("select * from t_label where id =#{id}") Label getById(Integer id); }
|
CacheNamespace注解说明:
1 2 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 中的缓存
源码解析链路
清除缓存
1 2 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()
|
获取缓存
1 2 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
|
保存二级缓存
1 2 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
1 2 3
| <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> </bean>
|
2、配置 MapperFactoryBean
1 2 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 对像执行业务方法
1 2 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
1 2 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的配置,都是一些常规事物配置:
1 2 3 4 5 6
| <!--添加事物配置--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource"/> </bean> <!--事物注解配置--> <tx:annotation-driven/>
|
添加事物注解:
1 2 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 当中,共下次查询使用。
相关源码:
1 2 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/>
|