mybatis缓存 redis实现

       最近项目需要针对mybatis查询加入缓存,使用redis,于是上网查找mybatis缓存 redis实现的相关文章,有很多关于mybatis redis缓存的介绍以及mybatis Cache接口的redis实现代码,但内容都是一致,看得出都转载某一人的手笔,虽然提供的代码逻辑是正确的,但是在项目应用中都存在问题。请小心使用!
        为什么网上流传的mybatis redis实现代码有问题,只要你调试过、测试过,你会发现有这么个问题存在:当页面对数据表存在CUD操作时,以往所有的查询缓存都会clear掉,当再次查询时,重新缓存;只有任意一个CUD操作,整个mybatis缓存都会清空,这样会带来相反的结果,不能利用缓存提供效率,反而降低了系统性能。我们的目标就是,针对Mybatis的Mapper.xml文件,当数据变动后,只清除变动语句存在mapper.xml里的查询缓存,而不是清除所有mapper.xml查询语句缓存。
       想了解这块的人,可以对比本文实现代码,和网上其他文章的实现代码,虽然缓存都是可用,但是对系统性能却存在极大差别。
     下面分享mybatis缓存 redis实现的步骤以及关键代码:
   1,开启mybatis二级缓存,<prop key="cacheEnabled">true</prop> ,在sqlSessionFactory配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">       
 <property name="dataSource" ref="dataSource" />       
 <!-- 配置sqlSessionFactory的参数 -->        
<property name="configurationProperties">            
  <props>                  
<prop key="cacheEnabled">true</prop>                  
 <!-- 查询时,关闭关联对象即时加载以提高性能  -->               
 <prop key="lazyLoadingEnabled">false</prop>                 
  <!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指定),
不会加载关联表的所有字段,以提高性能 -->                
 <prop key="aggressiveLazyLoading">true</prop>           
        <!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果  -->       
           <prop key="multipleResultSetsEnabled">true</prop>              
     <!-- 允许使用列标签代替列名 -->                   
<prop key="useColumnLabel">true</prop>                  
<!-- 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值),
数据表的PK生成策略将被覆盖 -->                   
<prop key="useGeneratedKeys">true</prop>                  
<!-- 给予被嵌套的resultMap以字段-属性的映射支持     -->                
<prop key="autoMappingBehavior">FULL</prop>                  
<!-- 对于批量更新操作缓存SQL以提高性能      -->                
<prop key="defaultExecutorType">BATCH</prop>                  
<!-- 数据库超过25000秒仍未响应则超时     -->               
 <prop key="defaultStatementTimeout">25000</prop>              
</props>        
</property>     
</bean>

   2,在需要添加缓存的mybatis mapper.xml文件中指定缓存Cache实现类,广泛采用LRU算法:

  <mapper namespace="com......Mapper">
    <cache eviction="LRU" type="com.xfx.service.cache.redis.MybatisRedisCache" />
   </mapper>

  3,mybatis redis缓存重点,Cache实现类(通过jedisPool获取jedis的方式你可以修改):
   MybatisRedisCache.java

import java.io.BufferedReader;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

import java.util.Properties;

import java.util.Set;

import java.util.concurrent.locks.ReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;


import org.apache.commons.codec.digest.DigestUtils;

import org.apache.ibatis.cache.Cache;

import org.apache.log4j.Logger;


import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import redis.clients.jedis.JedisPoolConfig;



/*

 * 使用第三方缓存服务器,处理二级缓存

 * <a href="http://www.zyiqibook.com">在一起 学习交流分享网 功能源码分享</a>

 * @author xfxpeter@gmail.com

 */

public class MybatisRedisCache implements Cache {

     

    private static final Logger logger = Logger.getLogger(MybatisRedisCache.class);

     

    /** The ReadWriteLock. */

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();



    private JedisPool jedisPool;

    private static final int DB_INDEX = 1;

    private final String COMMON_CACHE_KEY = "COM:";

    private static final String UTF_8 = "utf-8";

  

    /**

     * 按照一定规则标识key

     */

    private String getKey(Object key) {

        StringBuilder accum = new StringBuilder();

        accum.append(COMMON_CACHE_KEY);

        accum.append(this.id).append(":");

        accum.append(DigestUtils.md5Hex(String.valueOf(key)));

        return accum.toString();

    }

  

    /**

     * redis key规则前缀

     */

    private String getKeys() {

        return COMMON_CACHE_KEY + this.id + ":*";

    }



    private String id;



    private Properties properties;



    {

        properties = getProp();

        JedisPoolConfig config = new JedisPoolConfig();

        config.setMaxIdle(Integer.valueOf(properties

                .getProperty("redis.pool.maxIdle")));

        jedisPool = new JedisPool(config, properties.getProperty("redis.host"),

                Integer.valueOf(properties.getProperty("redis.port")),

                Integer.valueOf(properties.getProperty("redis.timeout")),

                properties.getProperty("redis.password"));

    }

     

    /**

     * 加载项目redis连接属性文件

     */

    private Properties getProp(){

        if(properties == null || properties.isEmpty()){

            String propName = "config.properties";

            properties = new Properties();

            InputStream is = null;

            BufferedReader bf = null;

            try {

                is= this.getClass().getResourceAsStream("/META-INF/config.properties");//将地址加在到文件输入流中

                bf = new BufferedReader(new InputStreamReader(is,"UTF-8"));//转为字符流,设置编码为UTF-8防止出现乱码

                properties.load(bf);//properties对象加载文件输入流

            } catch (UnsupportedEncodingException e) {

                logger.error(propName + "编码格式转换失败,不支持指定编码。" +  e);

            } catch (FileNotFoundException e) {

                logger.error(propName + "属性文件common.properties不存在。" +  e);

            } catch (IOException e) {

                logger.error(propName + "属性文件common.properties读取失败。" +  e);

            } catch (Exception e) {

                logger.error(propName + "属性文件common.properties读取失败。" +  e);

            } finally {

                try {//文件流关闭

                    if(bf != null){

                        bf.close();

                    }

                    if(is != null ){

                        is.close();

                    }

                } catch (IOException e) {

                    logger.error("关闭文件流失败。" +  e);

                }

            }

        }

        return properties;

    }



    public MybatisRedisCache() {

    }



    public MybatisRedisCache(final String id) {

        if (id == null) {

            throw new IllegalArgumentException("必须传入ID");

        }

        logger.debug("MybatisRedisCache:id=" + id);

        this.id = id;

    }



    @Override

    public String getId() {

        return this.id;

    }



    @Override

    public int getSize() {

        Jedis jedis = null;

        int result = 0;

        boolean borrowOrOprSuccess = true;

        try {

            jedis = jedisPool.getResource();

            jedis.select(DB_INDEX);

            Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF_8));

            if (null != keys && !keys.isEmpty()) {

                result = keys.size();

            }

            logger.debug(this.id+"---->>>>总缓存数:" + result);

        } catch (Exception e) {

            borrowOrOprSuccess = false;

            if (jedis != null)

                jedisPool.returnBrokenResource(jedis);

        } finally {

            if (borrowOrOprSuccess)

                jedisPool.returnResource(jedis);

        }

        return result;



    }



    @Override

    public void putObject(Object key, Object value) {

        Jedis jedis = null;

        boolean borrowOrOprSuccess = true;

        try {

            jedis = jedisPool.getResource();

            jedis.select(DB_INDEX);

             

            byte[] keys = getKey(key).getBytes(UTF_8);

            jedis.set(keys, SerializeUtil.serialize(value));

            logger.debug("添加缓存--------"+this.id);

            //getSize();

        } catch (Exception e) {

            borrowOrOprSuccess = false;

            if (jedis != null)

                jedisPool.returnBrokenResource(jedis);

        } finally {

            if (borrowOrOprSuccess)

                jedisPool.returnResource(jedis);

        }



    }



    @Override

    public Object getObject(Object key) {

        Jedis jedis = null;

        Object value = null;

        boolean borrowOrOprSuccess = true;

        try {

            jedis = jedisPool.getResource();

            jedis.select(DB_INDEX);

            value = SerializeUtil.unserialize(jedis.get(getKey(key).getBytes(UTF_8)));

            logger.debug("从缓存中获取-----"+this.id);

            //getSize();

        } catch (Exception e) {

            borrowOrOprSuccess = false;

            if (jedis != null)

                jedisPool.returnBrokenResource(jedis);

        } finally {

            if (borrowOrOprSuccess)

                jedisPool.returnResource(jedis);

        }

        return value;

    }



    @Override

    public Object removeObject(Object key) {

        Jedis jedis = null;

        Object value = null;

        boolean borrowOrOprSuccess = true;

        try {

            jedis = jedisPool.getResource();

            jedis.select(DB_INDEX);

            value = jedis.del(getKey(key).getBytes(UTF_8));

            logger.debug("LRU算法从缓存中移除-----"+this.id);

            //getSize();

        } catch (Exception e) {

            borrowOrOprSuccess = false;

            if (jedis != null)

                jedisPool.returnBrokenResource(jedis);

        } finally {

            if (borrowOrOprSuccess)

                jedisPool.returnResource(jedis);

        }

        return value;

    }



    @Override

    public void clear() {

        Jedis jedis = null;

        boolean borrowOrOprSuccess = true;

        try {

            jedis = jedisPool.getResource();

            jedis.select(DB_INDEX);

            Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF_8));

            logger.debug("出现CUD操作,清空对应Mapper缓存======>"+keys.size());

            for (byte[] key : keys) {

                jedis.del(key);

            }

            //下面是网上流传的方法,极大的降低系统性能,没起到加入缓存应有的作用,这是不可取的。

            //jedis.flushDB();

            //jedis.flushAll();

        } catch (Exception e) {

            borrowOrOprSuccess = false;

            if (jedis != null)

                jedisPool.returnBrokenResource(jedis);

        } finally {

            if (borrowOrOprSuccess)

                jedisPool.returnResource(jedis);

        }

    }



    @Override

    public ReadWriteLock getReadWriteLock() {

        return readWriteLock;

    }

}

   SerializeUtil.java

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;



/**

 * 序列化与反序列化

 * <a href="http://www.zyiqibook.com">在一起 学习交流分享网 功能源码分享</a>

 * @author xfxpeter@gmail.com

 */

public class SerializeUtil {

    public static byte[] serialize(Object object) { 

        if (null == object) return null;

        ObjectOutputStream oos = null; 

        ByteArrayOutputStream baos = null; 

        try { 

            //序列化 

            baos = new ByteArrayOutputStream(); 

            oos = new ObjectOutputStream(baos); 

            oos.writeObject(object); 

            byte[] bytes = baos.toByteArray(); 

            return bytes; 

        } catch (Exception e) { 

                e.printStackTrace(); 

        } 

        return null; 

     } 

            

    public static Object unserialize(byte[] bytes) { 

          if(null == bytes) return null;

        ByteArrayInputStream bais = null; 

        try { 

            //反序列化 

            bais = new ByteArrayInputStream(bytes); 

            ObjectInputStream ois = new ObjectInputStream(bais); 

            return ois.readObject(); 

        } catch (Exception e) { 

              e.printStackTrace();

           } 

        return null; 

     }

}

 redis缓存使用好了,对项目性能有极大改变,特别是大型web应用项目,这是大型项目性能优化的重点内容。
 本站其他一篇关于redis集成其他技术框架的文章请查看:shiro redis集群介绍 附代码贡献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值