下面是“Mybatis一级缓存和结合Spring Framework后失效的源码探究”的攻略:
Mybatis一级缓存
Mybatis自身提供了一级缓存的支持,即在同一次会话中多次查询同一条记录时,第一次查询时会将该数据缓存下来,后续再次查询时直接从缓存中取出,避免了重复的数据库查询操作,提升了性能。
对于一级缓存的使用,需要注意以下几点:
-
一级缓存的作用域是SqlSession,在同一个SqlSession中查询到的数据可以直接从缓存中获取。
-
默认情况下SqlSession开启一级缓存,即开启自动提交(autoCommit=false)情况下,每次执行查询语句后,查询结果都会被缓存起来。
-
如果缓存的数据被修改或删除,那么缓存数据会被清空。
Mybatis一级缓存与Spring Framework结合后失效的原因及解决方法
结合Spring Framework后,每个Mapper接口都会被Spring所代理,即每次调用Mapper接口时会生成一个新的代理类,因此Mapper接口的代理类是不同的,即使两个Mapper接口里的方法名和参数都一样。
这样导致了两个问题:
-
每个Mapper接口的代理类都有自己的SqlSession实例,不属于同一个SqlSession,因此会有多个缓存实例。
-
不同的Mapper接口的代理类中可能涉及同一个数据库表的数据,但这两个代理类实际上是不同的,因此可能出现一级缓存污染的现象。
解决以上问题,有3种方案:
-
方案一:使用Spring的事务管理机制,每次操作,保证只使用一个SqlSession。在Spring的配置文件中开启事务,使用@Transactional注解对需要使用SqlSession的方法进行标注。
-
方案二:使用Mybatis-Spring,Mybatis-Spring是Mybatis官方提供的与Spring集成的插件包,其中包含了SqlSessionTemplate,它提供统一的SqlSession操作入口,保证在同一事务中所有的操作只使用同一SqlSession实例。
-
方案三:关闭一级缓存。如果不想开启一级缓存,可以在配置文件中将localCacheScope设置为SESSION,即关闭一级缓存。
示例1
接下来的示例展示了如何基于Spring Framework和Mybatis-Spring来实现上述第二种方案中的解决方法。
- 首先,在applicationContext.xml中引入Mybatis-Spring。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
- 在Spring的配置文件中加入事务管理器的配置,使用tx:annotation-driven开启Spring事务注解支持。将SqlSessionFactory注入到事务管理器中。
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
...
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
- 在Mapper接口中使用SqlSessionTemplate操作数据库。
@Repository
public class UserMapper {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
public User findUserById(int id) {
return sqlSessionTemplate.selectOne("com.example.UserMapper.findUserById", id);
}
}
- 在Service实现类中使用@Transactional注解实现事务的控制。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public User findUserById(int id) {
return userMapper.findUserById(id);
}
}
示例2
下面的示例演示了开启自动提交的情况下,Mybatis一级缓存如何生效。
- 在SqlSessionFactory中开启自动提交(autoCommit=true)功能,设置缓存类型(cacheEnabled=true)。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.example.po"/>
<property name="mapperLocations" value="classpath*:com/example/mapper/*.xml"/>
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="cacheEnabled" value="true"/>
</bean>
</property>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
helperDialect=mysql
</value>
</property>
</bean>
</array>
</property>
<property name="autoCommit" value="true"/>
</bean>
- 在Mapper接口中使用@Select注解进行查询操作。
public interface UserMapper {
@Select("SELECT * FROM tb_user WHERE id = #{id}")
User findUserById(int id);
}
- 在Service中进行测试。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findUserById(int id) {
User user = userMapper.findUserById(id);
User user2 = userMapper.findUserById(id);
return user;
}
}
在这个示例中,第一次查询后,查询结果被缓存在了一级缓存中,第二次查询时直接从缓存中取出,而不是再次查询数据库。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Mybatis一级缓存和结合Spring Framework后失效的源码探究 - Python技术站