1. 程式人生 > >Redis 整合spring ,做mysql的快取

Redis 整合spring ,做mysql的快取

專案環境: 在SpringMVC + MyBatis + Mysql。Redis部署在Linux虛擬機器。

1、整體思路

  • 參考Ehcache實現MyBatis二級快取程式碼(Maven引用對應jar查閱)
  • 使用Spring管理Redis連線池
  • 模仿EhcacheCache,實現RedisCache

2、pom.xml中加入Maven

<!-- spring-redis實現 -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.2.RELEASE</version>
</dependency>
<!-- redis客戶端jar -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.0</version>
</dependency>
<!-- Ehcache實現,用於參考 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>


3、引入applicationContext.xml中引入redis配置

<!-- 引入資料庫配置檔案 -->
<bean id="propertyConfigurer"    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:jdbc.properties</value>
            <value>classpath:redis.properties</value>
        </list>
    </property>
</bean>
<!-- redis資料來源 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  
    <property name="maxIdle" value="${redis.maxIdle}" />  
    <property name="maxTotal" value="${redis.maxActive}" />  
    <property name="maxWaitMillis" value="${redis.maxWait}" />  
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
</bean>
<!-- Spring-redis連線池管理工廠 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"  
    p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"  p:pool-config-ref="poolConfig"/>  
<!-- 使用中間類解決RedisCache.jedisConnectionFactory的靜態注入,從而使MyBatis實現第三方快取 -->
<bean id="redisCacheTransfer" class="com.strive.cms.cache.RedisCacheTransfer">
    <property name="jedisConnectionFactory" ref="jedisConnectionFactory"/>
</bean>


4、建立快取實現類RedisCache

/**
 * 
 * @描述: 使用第三方記憶體資料庫Redis作為二級快取
 * @版權: Copyright (c) 2016 
 * @作者: xiad
 * @版本: 1.0 
 * @建立日期: 2016年3月2日 
 * @建立時間: 下午8:02:57
 */
public class RedisCache implements Cache
{
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    private static JedisConnectionFactory jedisConnectionFactory;

    private final String id;

    /**
     * The {@code ReadWriteLock}.
     */
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public RedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        logger.debug("MybatisRedisCache:id=" + id);
        this.id = id;
    }

    @Override
    public void clear()
    {
        JedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            connection.flushDb();
            connection.flushAll();
        }
        catch (JedisConnectionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
    }

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

    @Override
    public Object getObject(Object key)
    {
        Object result = null;
        JedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            result = serializer.deserialize(connection.get(serializer.serialize(key)));
        }
        catch (JedisConnectionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    @Override
    public ReadWriteLock getReadWriteLock()
    {
        return this.readWriteLock;
    }

    @Override
    public int getSize()
    {
        int result = 0;
        JedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            result = Integer.valueOf(connection.dbSize().toString());
        }
        catch (JedisConnectionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    @Override
    public void putObject(Object key, Object value)
    {
        JedisConnection connection = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            connection.set(serializer.serialize(key), serializer.serialize(value));
        }
        catch (JedisConnectionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
    }

    @Override
    public Object removeObject(Object key)
    {
        JedisConnection connection = null;
        Object result = null;
        try
        {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            result =connection.expire(serializer.serialize(key), 0);
        }
        catch (JedisConnectionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (connection != null) {
                connection.close();
            }
        }
        return result;
    }

    public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        RedisCache.jedisConnectionFactory = jedisConnectionFactory;
    }

}


5、建立中間類RedisCacheTransfer,完成RedisCache.jedisConnectionFactory的靜態注入

/**
 * 
 * @描述: 靜態注入中間類
 * @版權: Copyright (c) 2016 
 * @作者: xiad
 * @版本: 1.0 
 * @建立日期: 2016年3月2日 
 * @建立時間: 下午8:02:57
 */
public class RedisCacheTransfer 
{

    @Autowired
    public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
    }

}


6、配置檔案redis.properties

# Redis settings  
redis.host=192.168.25.132
redis.port=6379  
redis.pass=

redis.maxIdle=300  
redis.maxActive=600  
redis.maxWait=1000  
redis.testOnBorrow=true  


7、mapper中加入MyBatis二級快取

<mapper namespace="com.strive.cms.dao.site.CatalogMapper" >
  <cache type="com.strive.cms.cache.RedisCache"/>
  .....
</mapper>


8、Mybatis全域性配置

<?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>
    <!-- 配置mybatis的快取,延遲載入等等一系列屬性 -->
    <settings>

        <!-- 全域性對映器啟用快取 -->
        <setting name="cacheEnabled" value="true"/>

        <!-- 查詢時,關閉關聯物件即時載入以提高效能 -->
        <setting name="lazyLoadingEnabled" value="false"/>

        <!-- 對於未知的SQL查詢,允許返回不同的結果集以達到通用的效果 -->
        <setting name="multipleResultSetsEnabled" value="true"/>

        <!-- 允許使用列標籤代替列名 -->
        <setting name="useColumnLabel" value="true"/>

        <!-- 不允許使用自定義的主鍵值(比如由程式生成的UUID 32位編碼作為鍵值),資料表的PK生成策略將被覆蓋 -->
        <setting name="useGeneratedKeys" value="false"/>

        <!-- 給予被巢狀的resultMap以欄位-屬性的對映支援 FULL,PARTIAL -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>

        <!-- 對於批量更新操作快取SQL以提高效能 BATCH,SIMPLE -->
        <!-- <setting name="defaultExecutorType" value="BATCH" /> -->

        <!-- 資料庫超過25000秒仍未響應則超時 -->
        <!-- <setting name="defaultStatementTimeout" value="25000" /> -->

        <!-- Allows using RowBounds on nested statements -->
        <setting name="safeRowBoundsEnabled" value="false"/>

        <!-- Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names aColumn. -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>

        <!-- MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT 
            local session will be used just for statement execution, no data will be shared between two different calls to the same SqlSession. -->
        <setting name="localCacheScope" value="SESSION"/>

        <!-- Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers require specifying the column JDBC type but others work with generic values 
            like NULL, VARCHAR or OTHER. -->
        <setting name="jdbcTypeForNull" value="OTHER"/>

        <!-- Specifies which Object's methods trigger a lazy load -->
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>

        <!-- 設定關聯物件載入的形態,此處為按需載入欄位(載入欄位由SQL指 定),不會載入關聯表的所有欄位,以提高效能 -->
        <setting name="aggressiveLazyLoading" value="true"/>

    </settings>

</configuration>


9、列印Sql日誌,方便測試

#定義LOG輸出級別為INFO
log4j.rootLogger=INFO,Console,File

####定義日誌輸出目的地為控制檯
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
#可以靈活地指定日誌輸出格式,下面一行是指定具體的格式
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n

####檔案大小到達指定尺寸的時候產生一個新的檔案
log4j.appender.File = org.apache.log4j.RollingFileAppender
#指定輸出目錄
log4j.appender.File.File = logs/ssm.log
#定義檔案最大大小
log4j.appender.File.MaxFileSize = 10MB
#輸出所以日誌,如果換成DEBUG表示輸出DEBUG以上級別日誌
log4j.appender.File.Threshold = ALL
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =[%p] [%d{yyyy-MM-dd HH\:mm\:ss}][%c]%m%n

####顯示本專案SQL語句部分
log4j.logger.com.strive.cms=DEBUG


10、測試程式碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})  
public class MyBatisCacheSecondTest
{
    private static final Logger logger = LoggerFactory.getLogger(MyBatisCacheSecondTest.class);    

    @Autowired
    private  SiteService service;

    /*
     * 二級快取測試
     */
    @Test
    public void testCache2() {
        PageInfo<Site> page1 = service.querySite("", 1, 2, "", "");
        logger.info(page1.getList().get(1).getName());

        PageInfo<Site> page2 = service.querySite("", 2, 2, "", "");
        logger.info(page2.getList().get(0).getName());

        PageInfo<Site> page3 = service.querySite("", 1, 2, "", "");
        logger.info(page3.getList().get(0).getName());
    }   

}


首次執行結果 

這裡寫圖片描述
後續執行結果 
這裡寫圖片描述
同條件的查詢語句可以發現,已經不再查詢Mysql,而是直接取Redis資料 
檢視Redis資料庫 keys *, 會發現多了很多資料,結果如下 
這裡寫圖片描述
至此,Redis基本配置成功。