Spring + MySQL + Mybatis + Redis【二級緩存】

1、Redis環境java

Redis 官網 :http://redis.io/mysql

windows下載:https://github.com/dmajkic/redis/downloadslinux

一、文件解壓縮git

二、啓動Redis服務器github

 

三、啓動Redis客戶端redis

 

四、測試Redis緩存算法

redis-cli.exe -h 127.0.0.1 -p 6379spring

set keytest valuestest 根據key、value加入緩存sql

get keytest 根據key獲取值數據庫

flushall 清空全部緩存

五、設置Redis密碼

六、conf配置文件

#是否之後臺守護進程運行,默認爲no, 取值yes, no 
daemonize no                   

#pid文件存放路徑
pidfile /var/run/redis.pid    

#配置redis端口,默認6379
port 6379                    

#綁定ip。默認綁定全部本機ip,通常用在服務器多ip下,能夠只監聽內網服務器ip,保證服務安全
bind 127.0.0.1              

#sock文件 
unixsocket /tmp/redis.sock  

#客戶端超時時間,單位秒 
timeout 300                

#log級別,支持四個級別,debug,notice,verbose,warning 
loglevel verbose           

#log文件路徑
logfile                    

#log輸出到標準設備,logs不寫文件,輸出到空設備,/deb/null
logfile stdout               

#保存快照的頻率,在多長時間內執行必定數量的寫操做時,保存快照的頻率,能夠設置多個條件。若是都註釋掉,則不作內存數據持久化。若是隻是把redis只用做cache,不開啓持久化功能
save <seconds> <changes> 
save 900 1 

#是否使用壓縮
rdbcompression            

#快照數據庫名稱 
dbfilename               

#數據庫存放路徑 
dir                       

#redis主從 作法  在從上填上主的IP和端口號 主上不用作任何設置
slaveof <masterip> <masterport>   

#主庫服務器口令,若是主服務器未打開requirepass,則不須要此項
masterauth <master-password>     

#在master服務器掛掉或者同步失敗時,從服務器是否繼續提供服務
slave-serve-stale-data yes         

#設置redis服務密碼,若是開啓,則客戶端鏈接時須要 -a  指定密碼,不然操做會提示無權限
requirepass foobared               

#命令更名,至關於linux  alias,能夠用改功能屏蔽一些危險命令
rename-command                    

#最大鏈接數;0 表示不限制
maxclients 128                   

#最大使用內存(分配的內存),推薦生產環境下作相應調整,咱們用的是隻用來作高速緩存,限制2G。默認狀況下,redis會佔用可用的全部內存
maxmemory <bytes>                

#過時策略,提供六種策略  
maxmemory-policy volatile-lru   
volatile-lru    //刪除過時和lru 的key(默認值) 
allkeys-lru     //刪除lru算法的key  
volatile-random //隨機刪除即將過時key  
allkeys->random //隨機刪除 
volatile-ttl    //刪除即將過時的  
noeviction      //永不過時,返回錯誤 

#是否開啓appendonlylog,開啓的話每次寫操做會記一條log。至關於mysql的binlog;不一樣的是,每次redis啓動都會讀此文件構建完整數據。即便刪除rdb文件,數據也是安全的 
appendonly    

#日誌文件的名稱,默認appendonly.aof
appendfilename appendonly.aof   

#異步寫append file 的策略。相似mysql事物log寫方式。三種
appendfsync                       
appendfsync always               //同步,每次寫都要flush到磁盤,安全,速度慢。 
appendfsync everysec             //每秒寫(默認值,推薦值)同mysql 
appendfsync no                   //交給操做系統去作flush的動做 

#虛擬內存開關 
vm-enabled no               

#swap文件,不一樣redis swap文件不能共享。並且生產環境下,不建議放在tmp目錄
vm-swap-file /tmp/redis.swap     

#vm大小限制。0:不限制,建議60-80% 可用內存大小
vm-max-memory 0                 

#根據緩存內容大小調整,默認32字節
vm-page-size 32                

#page數。每 8 page,會佔用1字節內存。vm-page-size * vm-pages 等於 swap 文件大小
vm-pages 134217728              

#vm 最大io線程數。注意: 0 標誌禁止使用vm 
vm-max-threads 4

2、實現Redis緩存

一、總體思路

  • 參考Ehcache實現MyBatis二級緩存代碼(Maven引用對應jar查閱)
  • 使用Spring管理Redis鏈接池
  • 模仿EhcacheCache,實現RedisCache

二、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> 

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

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 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.hsmdata.springTest.modules.cache.RedisCacheTransfer"> <property name="jedisConnectionFactory" ref="jedisConnectionFactory"/> </bean> </beans>

四、redis.properties配置文件

#============================#
#==== Redis settings ====#
#============================#
#redis 服務器 IP
redis.host=127.0.0.1

#redis 服務器端口
redis.port=6379

#redis 密碼
redis.pass=

#redis 支持16個數據庫(至關於不一樣用戶)可使不一樣的應用程序數據彼此分開同時又存儲在相同的實例上
redis.dbIndex=3

#redis 緩存數據過時時間單位秒(3600*12 = 43 200)
redis.expiration=43200

#控制一個 pool 最多有多少個狀態爲 idle 的jedis實例
redis.maxIdle=200

#控制一個 pool 可分配多少個jedis實例
redis.maxActive=1000

#當borrow一個jedis實例時,最大的等待時間,若是超過等待時間,則直接拋出JedisConnectionException;
redis.maxWait=500

#在borrow一個jedis實例時,是否提早進行alidate操做;若是爲true,則獲得的jedis實例均是可用的;
redis.testOnBorrow=true

五、建立緩存實現類RedisCache

package com.hsmdata.springTest.modules.cache;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import redis.clients.jedis.exceptions.JedisConnectionException;

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

/**
 * @author 
 * 2018-04-10 20:50
 * $DESCRIPTION}
 */

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;
    }

}
View Code

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

package com.hsmdata.springTest.modules.cache;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

/**
 * @author 
 * 2018-04-10 20:52
 * $DESCRIPTION}
 */

public class RedisCacheTransfer {
    @Autowired
    public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
        RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
    }
}
View Code

七、mapper中加入MyBatis二級緩存

<mapper namespace="com.hsmdata.springTest.modules.mapper.UserMapper" >
  <!--開啓本mapper的二級緩存,隔10秒自動刷新緩存 flushInterval="10000" -->
  <cache type="com.hsmdata.springTest.modules.cache.RedisCache" />

八、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>
    <settings>
        <!-- 開啓二級緩存,默認是false -->
        <setting name="cacheEnabled" value="true"/>

        <!--resultMap中的association和collection標籤具備延遲加載的功能。-->
        <!--延遲加載的意思是說,在關聯查詢時,利用延遲加載,先加載主信息。使用關聯信息時再去加載關聯信息。-->

        <!-- lazyLoadingEnabled:延遲加載啓動,默認是false
        全局性設置懶加載。若是設爲‘false’,則全部相關聯的都會被初始化加載。-->
        <setting name="lazyLoadingEnabled" value="false"/>

        <!-- aggressiveLazyLoading:積極的懶加載,false的話按需加載,默認是true
        當設置爲‘true’的時候,懶加載的對象可能被任何懶屬性所有加載。不然,每一個屬性都按需加載。-->
        <setting name="aggressiveLazyLoading" value="true"/>

        <setting name="multipleResultSetsEnabled" value="true"/> 
        <setting name="useColumnLabel" value="true"/> 
        <setting name="useGeneratedKeys" value="true"/>
        <setting name="defaultExecutorType" value="SIMPLE"/> 
        <setting name="defaultStatementTimeout" value="25000"/>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>
View Code

九、測試

package com.springTest.mybatis.cache;

import com.hsmdata.springTest.modules.entity.User;
import com.hsmdata.springTest.modules.mapper.UserMapper;
import com.hsmdata.springTest.modules.service.UserService;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author 
 * 2018-04-10 15:11
 * $DESCRIPTION}
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/config/applicationContext.xml", "classpath:/config/spring-servlet.xml"})
public class CacheTest {
    @Autowired
    private UserService userService;
    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 同一個sqlSession
     */
    @Test
    public  void testFirstCache(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
        User user=userMapper.selectByPrimaryKey(56);
        System.out.println(user);

/*       對sqlsession執行commit操做,也就意味着用戶執行了update、delete等操做,那麼數據庫中的數據勢必會發生變化,若是用戶請求數據仍然使用以前內存中的數據,那麼將讀到髒數據。
        因此在執行sqlsession操做後,會清除保存數據的HashMap,用戶在發起查詢請求時就會從新讀取數據並放入一級緩存中了。*/
     //   sqlSession.commit();

        user=userMapper.selectByPrimaryKey(56);
        System.out.println(user);
    }

    /**
     * 不一樣的sqlSession
     */
    @Test
    public  void testSecondaryCache(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper= sqlSession.getMapper(UserMapper.class);
        User user=userMapper.selectByPrimaryKey(56);
        System.out.println(user);

//        即便開啓了二級緩存,不一樣的sqlsession之間的緩存數據也不是想互訪就能互訪的,必須等到sqlsession關閉了之後,纔會把其一級緩存中的數據寫入二級緩存。
//        關閉session
//        sqlSession.close();

//        經過sqlSessionFactory建立一個新的session
        sqlSession=sqlSessionFactory.openSession();
//        獲取mapper對象
        userMapper=sqlSession.getMapper(UserMapper.class);

        user=userMapper.selectByPrimaryKey(56);
        System.out.println(user);
    }

    /**
     * 不一樣的sqlSession
     */
    @Test
    public void testSecondaryCache2() {
       /* User user=new User("cache","123456","cache","","male",20);
        userService.insert(user);*/

        User user = userService.get(56);
        System.out.println(user);

        User user1 = userService.get(56);
        System.out.println(user1);
    }

    @Test
    public void testFirstCache2() {
       /* User user=new User("cache","123456","cache","","male",20);
        userService.insert(user);*/

        User user = userService.getTwo(56);
        System.out.println(user);
    }

    @Test
    public void testRedisCache(){
        User user=userService.get(55);
        System.out.println(user);

        User user2=userService.get(56);
        System.out.println(user2);

        User user3=userService.get(55);
        System.out.println(user3);

    }

}
相關文章
相關標籤/搜索