1. 程式人生 > >SpringBoot+Mybatis+redis實現二級緩存

SpringBoot+Mybatis+redis實現二級緩存

gap nal configure argument prope gets ssp tro 1.2

對於查詢比較多的項目可以考慮配置二級緩存,mybatis本身的二級緩存是緩存到本地,但是對於多個節點的項目來說,可能會出現數據不一致的問題,所以采用redis緩存,這樣二級緩存的數據就可以緩存到內存,可實現多個節點項目的數據同步。

1、配置redis的連接

#redis
gmall.redis.host=172.16.1.250
gmall.redis.port=6379
gmall.redis.pass=Gworld2017
gmall.redis.photo.database=6

#最大分配的對象數  
gmall.redis.maxActive=12000

#最大能夠保持idel狀態的對象數 
gmall.redis.maxIdle
=600 #當池內沒有返回對象時,最大等待時間 gmall.redis.maxWait=2000 gmall.redis.timeout=5000 #當調用borrow Object方法時,是否進行有效性檢查 gmall.redis.testOnBorrow=true #當調用return Object方法時,是否進行有效性檢查 gmall.redis.testOnReturn=true gmall.redis.minIdle=5
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd" default-lazy-init="false"> <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" primary="true"> <property name="hostName" value="${gmall.redis.host}" /> <property name="port" value="${gmall.redis.port}" /> <property name="password" value="${gmall.redis.pass}" /> <property name="timeout" value="${gmall.redis.timeout}" /> <property name="database" value="${gmall.redis.photo.database}" /> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" primary="true"> <property name="connectionFactory" ref="jedisConnFactory" /> <property name="exposeConnection" value="true" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> </bean> </beans>

2、配置mybatis-config.xml文件

  

<?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="cacheEnabled" value="true"/>
    </settings>
</configuration>

3、編寫RedisCache實現Cache類

package com.gcard.gwmedia.cache;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import com.gcard.gwmedia.utils.SpringBeanFactoryUtils;

public class RedisCache implements Cache {
    
    Logger logger = LoggerFactory.getLogger(getClass());

    private final String FREFIX = "CACHE_";
    private final String id;
    
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    
    @SuppressWarnings("rawtypes")
    private RedisTemplate redisTemplate;
    
    private long cleanInterval;
    
    private long lastClear;
    
    public RedisCache(String id){
        if(id == null){
            throw new IllegalArgumentException("Cache instance require an ID");
        }
        this.id = id;
        this.cleanInterval = 1 * 60 * 60 * 1000;//一個小時
        this.lastClear = System.currentTimeMillis();
    }

    @SuppressWarnings({ "unchecked" })
    @Override
    public void clear() {
        lastClear = System.currentTimeMillis();
        String strKey = FREFIX + id.toString();
        getRedisTemplate().delete(strKey);
        logger.debug("Clear all the cached query result from redis");
    }

    @Override
    public String getId() {
        return id;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public Object getObject(Object key) {
        if(key != null){
            String strKey = FREFIX + id.toString();
            if(clearWhenStale(strKey)){
                return null;
            }
            RedisTemplate redisTemplate = getRedisTemplate();
            Object obj = redisTemplate.opsForHash().get(strKey, key.toString());
            logger.debug("Get cached query result from redis");
            if(obj != null){
                return obj;
            }else{
                return null;
            }
        }else{
            return null;
        }
    }

    @SuppressWarnings("rawtypes")
    private RedisTemplate getRedisTemplate() {
        if(redisTemplate == null){
            redisTemplate = (RedisTemplate) SpringBeanFactoryUtils.getBean("redisTemplate");
        }
        return redisTemplate;
    }
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public void putObject(Object key, Object value) {
        if (key != null) {
            String strKey = FREFIX + id.toString();
            clearWhenStale(strKey);
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.opsForHash().put(strKey,key.toString(),value);
            logger.debug("Put query result to redis");
        }
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }

    @Override
    public int getSize() {
        return 0;
    }


    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public Object removeObject(Object key) {
        if (key != null) {
            String strKey = FREFIX + id.toString();
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.opsForHash().delete(strKey, key.toString());
            logger.debug("Remove cached query result from redis");
        }
        return null;
    }
    public boolean clearWhenStale(Object key){
        if(System.currentTimeMillis()-lastClear > cleanInterval){
            logger.info("clearWhenStale key={}", key);
            clear();
            return true;
        }
        return false;
        
    }
}

4、配置mapper.xml文件

在mapper.xml文件加入<cache/>

<cache type="com.gcard.gwmedia.cache.RedisCache"/>

5、Application啟動類添加註解@EnableCaching

package com.gcard.gwmedia;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ImportResource;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@ImportResource(locations = {"classpath:/config/applicationContext.xml"})
@EnableAsync
@EnableCaching
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
}

SpringBoot+Mybatis+redis實現二級緩存