springboot配置mybatis redis缓存
一、概述
首先來了解下mybatis 緩存,mybatis緩存分為一級緩存和二級緩存。一級緩存是默認開啟的,無需其他配置操作,二級緩存則需要手動設置開啟。
一級緩存原理:
Mybatis的一級緩存是指同一個SqlSession中的操作。一級緩存的作用域是一個SqlSession。
在同一個SqlSession中,執行相同的查詢SQL,第一次會去查詢數據庫,并寫到緩存中;第二次直接從緩存中取。當執行SQL時兩次查詢中間發生了增刪改操作,則SqlSession的緩存清空。
二級緩存原理:
Mybatis的二級緩存是指mapper映射文件。二級緩存是多個sqlSession共享的,其作用域是mapper下的同一個namespace。
在不同的sqlSession中,相同的namespace下,相同的查詢sql語句并且參數也相同的情況下,會命中二級緩存。如果調用相同namespace下的mapper映射文件中的增刪改SQL,并執行了commit操作。此時會清空該namespace下的二級緩存。
二、代碼實現
了解一些基本原理后,我們開始在springboot集成mybatis的情況下,開啟二級緩存。
我們采用的redis序列化方式是默認的jdk序列化。所以數據庫的查詢對象(比如Student類)需要實現Serializable接口。
public class Student implements Serializable {//采用的redis序列化方式是默認的jdk序列化。所以數據庫的查詢對象實體需要實現Serializable接口。private static final long serialVersionUID = 1L;private int id;private String name;private int age;private String position;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", position='" + position + '\'' +'}';} }?
4、先看一下Redis的配置類(這里用的是lettuce)
@Configuration public class RedisConfig {@Autowiredprivate LettuceConnectionFactory connectionFactory;@Beanpublic RedisTemplate<String,Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();initDomainRedisTemplate(redisTemplate, connectionFactory);return redisTemplate;}/*** 設置數據存入 redis 的序列化方式* @param template* @param factory*/private void initDomainRedisTemplate(RedisTemplate<String, Object> template,LettuceConnectionFactory factory) {// 定義 key 的序列化方式為 string// 需要注意這里Key使用了 StringRedisSerializer,那么Key只能是String類型的,不能為Long,Integer,否則會報錯拋異常。StringRedisSerializer redisSerializer = new StringRedisSerializer();template.setKeySerializer(redisSerializer);// 定義 value 的序列化方式為 json@SuppressWarnings({"rawtypes", "unchecked"})Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setValueSerializer(jackson2JsonRedisSerializer);//hash結構的key和value序列化方式template.setHashKeySerializer(jackson2JsonRedisSerializer);template.setHashValueSerializer(jackson2JsonRedisSerializer);template.setEnableTransactionSupport(true);template.setConnectionFactory(factory);} }5、緩存配置類
public class MybatisRedisCache implements Cache {private static final Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);private String id;private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis過期時間public MybatisRedisCache(String id) {this.id = id;}private RedisTemplate<Object, Object> getRedisTemplate(){return ApplicationContextHolder.getBean("redisTemplate");}@Overridepublic String getId() {return id;}@Overridepublic void putObject(Object key, Object value) {RedisTemplate redisTemplate = getRedisTemplate();redisTemplate.boundHashOps(getId()).put(key, value);log.info("[結果放入到緩存中: " + key + "=" + value+" ]");}@Overridepublic Object getObject(Object key) {RedisTemplate redisTemplate = getRedisTemplate();Object value = redisTemplate.boundHashOps(getId()).get(key);log.info("[從緩存中獲取了: " + key + "=" + value+" ]");return value;}@Overridepublic Object removeObject(Object key) {RedisTemplate redisTemplate = getRedisTemplate();Object value = redisTemplate.boundHashOps(getId()).delete(key);log.info("[從緩存刪除了: " + key + "=" + value+" ]");return value;}@Overridepublic void clear() {RedisTemplate redisTemplate = getRedisTemplate();redisTemplate.delete(getId());log.info("清空緩存!!!");}@Overridepublic int getSize() {RedisTemplate redisTemplate = getRedisTemplate();Long size = redisTemplate.boundHashOps(getId()).size();return size == null ? 0 : size.intValue();}@Overridepublic ReadWriteLock getReadWriteLock() {return readWriteLock;} }?ps:
重點部分就是重寫這個mybatis的cache類,它只會對配置文件類型的映射文件起作用。
該接口共有以下五個方法:
String getId():mybatis緩存操作對象的標識符。一個mapper對應一個mybatis的緩存操作對象。
void putObject(Object key, Object value):將查詢結果塞入緩存。
Object getObject(Object key):從緩存中獲取被緩存的查詢結果。
Object removeObject(Object key):從緩存中刪除對應的key、value。只有在回滾時觸發。
void clear():發生更新時,清除緩存。
int getSize():可選實現。返回緩存的數量。
ReadWriteLock getReadWriteLock():可選實現。用于實現原子性的緩存操作。
上述重寫cache類中有幾個關鍵點:
- 自定義實現的二級緩存,必須要有一個帶id的構造函數,否則會報錯。
- 此處使用Spring封裝的redisTemplate來操作Redis。很多都是直接用jedis庫,但是現在springboot2.x 以上對lettuce的兼容更好。RedisTemplate封裝了底層的實現,使用redisTemplate會更加方便,無論是使用jedis還是使用lettuce,我們可以直接更換底層的庫,無需修改上層代碼。
- 這里不能通過@Autowire的方式引用redisTemplate,因為RedisCache并不是Spring容器里的bean。所以我們需要手動地去調用容器的getBean方法來拿到這個bean,那么這樣,我們就需要引入ApplicationContextHolder這個類。
?
@Component public class ApplicationContextHolder implements ApplicationContextAware{private static ApplicationContext applicationContext;/*** 實現ApplicationContextAware接口的context注入函數, 將其存入靜態變量.*/public void setApplicationContext(ApplicationContext applicationContext) {ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR}/*** 取得存儲在靜態變量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {checkApplicationContext();return applicationContext;}/*** 從靜態變量ApplicationContext中取得Bean, 自動轉型為所賦值對象的類型.*/@SuppressWarnings("unchecked")public static <T> T getBean(String name) {checkApplicationContext();return (T) applicationContext.getBean(name);}/*** 從靜態變量ApplicationContext中取得Bean, 自動轉型為所賦值對象的類型.*/@SuppressWarnings("unchecked")public static <T> T getBean(Class<T> clazz) {checkApplicationContext();return (T) applicationContext.getBeansOfType(clazz);}/*** 清除applicationContext靜態變量.*/public static void cleanApplicationContext() {applicationContext = null;}private static void checkApplicationContext() {if (applicationContext == null) {throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義SpringContextHolder");}} }7、然后再映射文件中開啟二級緩存(使用二級緩存)
<mapper namespace="com.example.demo.dao.StudentDao"><!-- 開啟基于redis的二級緩存 --><cache type="com.example.demo.redis.cache.MybatisRedisCache"/><cache/><insert id="insert" parameterType="com.example.demo.entity.Student" useGeneratedKeys="true" keyProperty="id">insert intostudents(name,age,position)values(#{name},#{age},#{position})</insert><insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">insert intostudents(name,age,position)values<foreach collection="studentList" item="item" index="index" open="" close="" separator=",">(#{item.name},#{item.age},#{item.position})</foreach></insert><delete id="delete" parameterType="java.lang.String">delete from students where name = #{name}</delete><!--并且在update語句中,設置flushCache為true,這樣在更新信息時,能夠自動失效緩存(本質上調用的是clear方法)--><update id="update" parameterType="com.example.demo.entity.Student" flushCache="true">update studentsset students.position = #{position}where name = #{name}</update><select id="findByName" resultMap="BaseResultMap">select *from studentswhere name = #{name}</select><select id="findAll" resultMap="BaseResultMap">select *from students</select><resultMap id="BaseResultMap" type="com.example.demo.entity.Student"><result column="name" property="name"/><result column="age" property="age"/><result column="position" property="position"/></resultMap> </mapper>下面是我在實現二級緩存過程中一些報錯問題:
總結
以上是生活随笔為你收集整理的springboot配置mybatis redis缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据处理神器Beam
- 下一篇: 苹果美区App Sore礼品卡防止被封教