Hexo

点滴积累 豁达处之

0%

Mybatis缓存、及集成spring

Mybatis缓存、及集成spring

简介

Mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解

Mybatis_cache01

一级缓存: 是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#selectList()
  >org.apache.ibatis.executor.CachingExecutor#query()
   >org.apache.ibatis.executor.BaseExecutor#query()
    >org.apache.ibatis.cache.impl.PerpetualCache#getObject

缓存的存储:

1
2
3
4
5
6
>mapper.mapper.selectById(23)
 >org.apache.ibatis.session.defaults.DefaultSqlSession#selectList()
  >org.apache.ibatis.executor.CachingExecutor#query()
   >org.apache.ibatis.executor.BaseExecutor#query()
    >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#selectByid
>org.apache.ibatis.binding.MapperProxy#invoke
>org.mybatis.spring.SqlSessionTemplate#selectOne(java.lang.String)
>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/>