SSM+Redis,使用Redis作为mybatis的二级缓存

       本文主要介绍将ssm项目与Redis进行整合,使用Redis作为缓存。其原理就是实现mybatis的自定义缓存,mybatis允许我们使用自定义的缓存来作为它的二级缓存,只需要实现它的Cache接口即可。
主要步骤如下:
0.ssm项目搭建(本文省略)
1.pom.xml里引入依赖
2.配置Redis的链接
3.自定义实现Cache

在这里可以根据自己的业务需要去实现cache。如:网上很多示例在clear时,使用flushDb(),这样会把所有缓存清除!而我只想删除相应mapper下的缓存,因此使用了list来存储和删除缓存,但是这样要注意在多表查询时会出现的脏数据等问题。

4.在mapper里开启二级缓存


1.pom.xml

加入Redis的依赖

<dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>2.9.0</version>
      </dependency>
      <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-redis</artifactId>
          <version>1.6.2.RELEASE</version>
</dependency>

2.Redis配置

在spring的配置文件里加入

<bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <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" />
    <!-- 将jedisConnectionFactory 注入到自定义缓存中 -->
    <bean id="RedisCache" class="cn.coolwind.redis.core.config.RedisCache">
        <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
    </bean>

redis.properties如下

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

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

3.自定义cache

在这里定义一个RedisCache类,根据自己的需要去实现Cache,分别是:
·String getId();//获取id
·void putObject(Object var1, Object var2);//写入缓存
·Object getObject(Object var1);//获取缓存
·Object removeObject(Object var1);//删除缓存
·void clear();//清空缓存
·int getSize();//获取大小
·ReadWriteLock getReadWriteLock();//获取ReadWriteLock
 
注意:
当进行insert、update操作时,会先去清空缓存,也就是调用clear方法,如果使用的是redisConnection.flushDb(),这样会把所有的缓存都清除,不符合我的需求。
 

我的方式:
因为每个namespace下的cache的id是不同的,每当调用putObject写入缓存时,可以使用一个list,将其key都存储在当前id的list下,当执行clear时,清除这个list下的缓存即可

    
具体代码如下:

public class RedisCache implements Cache {
    @Autowired
    private static JedisConnectionFactory jedisConnectionFactory;

    private String id;

    private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public RedisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("id is null");
        }
        this.id = id;
    }

    public RedisCache() {

    }

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

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

    @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));
            connection.lPush(serializer.serialize(id),serializer.serialize(key));
            System.out.println("写入缓存:" + key + "," + value);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }

    @Override
    public Object getObject(Object key) {
        Object res = null;
        JedisConnection connection = null;
        try {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            res = serializer.deserialize(connection.get(serializer.serialize(key)));
            if (res != null) {
                System.out.println("获取缓存数据:" + res.toString());
            } else {
                System.out.println("当前没有缓存:" + key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
        return res;
    }

    @Override
    public Object removeObject(Object key) {
        Object res = null;
        JedisConnection connection = null;
        try {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
            res = connection.expire(serializer.serialize(key), 0);
            connection.lRem(serializer.serialize(id),0,serializer.serialize(key));
            System.out.println("删除缓存:" + key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
        return res;
    }

    @Override
    public void clear() {
        JedisConnection connection = null;
        try {
            connection = jedisConnectionFactory.getConnection();
            RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
//            connection.flushDb();
//            connection.flushAll();
            Long length = connection.lLen(serializer.serialize(id));
            if (0 == length) {
                return;
            }
            List<byte[]> keys = connection.lRange(serializer.serialize(id),0,length-1);
            for (byte[] key :keys) {
                connection.expireAt(key,0);
                System.out.println("删除缓存:"+serializer.deserialize(key).toString());
            }
            connection.expireAt(serializer.serialize(id),0);
            keys.clear();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }

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

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


4.开启二级缓存

在mapper里加入:

<cache eviction="LRU" type="cn.coolwind.redis.core.config.RedisCache"/>

测试

准备了两个类:Test和Test2,分别对应两个不同的namespace
做如下操作:
1.分别查询Test和Test2
2.再次查询Test和Test2
3.对Test做修改操作
4.查询Test和Test2
得到如下打印结果:
缓存结果信息

从上面的打印结果可以看到:

  • 当第一次查询的时候,没有缓存,会往Redis写入缓存
  • 再次进行查询的时候,直接获取缓存数据
  • 对Test进行修改时,会删除Test的缓存
  • 再次查询的时候,Test会重新写入缓存,而Test2仍然在缓存中

这样清空缓存的作用域就是所在的namespace了;
如果使用redisConnection.flushDb()的话,有修改的时候Test和Test2的缓存都会被删除;
但是尽量不要使用多表操作,避免脏数据等问题。


代码下载:
https://github.com/fi00wind/ssm-redis

猜你喜欢

转载自blog.csdn.net/coolwindd/article/details/83541355