zoukankan      html  css  js  c++  java
  • mybatis整合redis二级缓存

    mybatis默认开启了二级缓存功能,在mybatis主配置文件中,将cacheEnabled设置成false,则会关闭二级缓存功能

      <settings>
        <!--二级缓存默认开启,false关闭-->
        <setting name="cacheEnabled" value="false" />
        <!--mybatis日志打印到控制台-->
        <setting name="logImpl" value="STDOUT_LOGGING" />
      </settings>

    mybatis框架虽然默认开启了二级缓存功能,但是并没有默认实现,也就是下面这句代码返回null, 然后走一级缓存

    下面是配置Redis作为mybatis的二级缓存,代码如下:

    (1)mybatis主配置文件mybatis-config.xml中添加如下配置

      <settings>
        <!--二级缓存默认开启,false关闭-->
        <setting name="cacheEnabled" value="true" />
        <!--mybatis日志打印到控制台,以便于观察-->
        <setting name="logImpl" value="STDOUT_LOGGING" />
      </settings>

    (2)FemaleMapper.xml 配置如下

    <mapper namespace="qinfeng.zheng.FemaleMapper">
        <select id="getFemaleById" resultType="qinfeng.zheng.entity.Female" parameterType="int">
          SELECT id,name FROM tb_female where id = #{id};
        </select>
        <cache eviction="LRU" type="qinfeng.zheng.RedisCache"/>
    </mapper>

    LRU是mybatis默认的过期策略

    (3)qinfengzhengRedisCache.java

    import org.apache.ibatis.cache.Cache;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.util.Map;
    
    /**
     * 使用redis的Hash结构
     */
    public class RedisCache implements Cache {
    
        private String id;
        private JedisPool jedisPool;
        private static int EXPIRE_TIME = 1000 * 1000;
    
        public RedisCache(String id) {
            if (id == null) {
                throw new IllegalArgumentException("Cache instances require an ID");
            }
            this.id = id;
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPool = new JedisPool(jedisPoolConfig, "192.168.79.221", 6379, 3000, "123456");
        }
    
        @Override
        public String getId() {
            return id;
        }
    
        @Override
        public void putObject(Object key, Object value) {
            execute(jedis -> {
                try {
                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
                    ObjectOutputStream outputStream = new ObjectOutputStream(stream);
                    outputStream.writeObject(value);
                    byte[] idBytes = id.getBytes();
                    jedis.hset(idBytes, key.toString().getBytes(), stream.toByteArray());
                    // 设置一个过期时间。。
                    if (jedis.ttl(idBytes) == -1) {
                        jedis.expire(idBytes, EXPIRE_TIME);
                    }
                } catch (Exception e) {
                    throw new RuntimeException("put object to redis error!", e);
                }
                return null;
            });
        }
    
        @Override
        public Object getObject(Object key) {
            return execute(jedis -> {
                try {
                    byte[] bytes = jedis.hget(id.getBytes(), key.toString().getBytes());
                    if (bytes == null) {
                        return null;
                    }
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                    ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
                    return objectInputStream.readObject();
                } catch (Exception e) {
                    throw new RuntimeException("get data from redis error!", e);
                }
            });
        }
    
        @Override
        public Object removeObject(Object key) {
            return execute(jedis -> jedis.hdel(id.getBytes(), key.toString().getBytes()));
        }
    
        @Override
        public void clear() {
            execute(jedis -> jedis.del(id.getBytes()));  //因为id是String类型,所以直接jedis.del(id)也行
        }
    
        @Override
        public int getSize() {
            return (Integer) execute(jedis -> {
                Map<byte[], byte[]> map = jedis.hgetAll(id.getBytes());
                return map.size();
            });
        }
    
        private Object execute(RedisCallBack redisCallBack) {
            try (Jedis jedis = jedisPool.getResource()) {
                return redisCallBack.callBack(jedis);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }
    }
    public interface RedisCallBack {
        Object callBack(Jedis jedis);
    }

    使用redis作为mybatis的二级缓存框架,最主要就是实现mybatis提供的spi接口--Cache即可,然后实现putObject,getObject这两个主要的方法即可。

    除此之外,记得pom.xml文件中添加jedis依赖

          <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>2.9.0</version>
            </dependency>

    (4)修改测试代码

    public class V1Test {
        public static void main(String[] args) {
            try (InputStream is = Resources.getResourceAsStream("mybatis-config.xml")) {
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
                SqlSession sqlSession = sqlSessionFactory.openSession(true);
                FemaleMapper femaleMapper = sqlSession.getMapper(FemaleMapper.class);
                Female female = femaleMapper.getFemaleById(1);
                System.out.println(female);
                System.out.println("-----------分隔符----------------------------------");
                SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
                FemaleMapper femaleMapper2 = sqlSession2.getMapper(FemaleMapper.class);
                Female female2 = femaleMapper2.getFemaleById(1);
                System.out.println(female2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    第一次运行:

    Opening JDBC Connection
    Created connection 331510866.
    ==>  Preparing: SELECT id,name FROM tb_female where id = ?; 
    ==> Parameters: 1(Integer)
    <==    Columns: id, name
    <==        Row: 1, soga
    <==      Total: 1
    Female(id=1, name=soga)
    -----------分隔符----------------------------------
    Opening JDBC Connection
    Created connection 1316061703.
    ==>  Preparing: SELECT id,name FROM tb_female where id = ?; 
    ==> Parameters: 1(Integer)
    <==    Columns: id, name
    <==        Row: 1, soga
    <==      Total: 1
    Female(id=1, name=soga)

    哈哈,貌似还是查库两次,redis缓存好像没有起到作用。

    事实上,mybatis框架为二级缓存提供了一个缓冲Map, 需要在调用commit方法之后,才会将缓冲区Map中的数据写入到redis中。

    我们修改一下测试代码,然后再运行观察结果

    public class V1Test {
        public static void main(String[] args) {
            try (InputStream is = Resources.getResourceAsStream("mybatis-config.xml")) {
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
                SqlSession sqlSession = sqlSessionFactory.openSession(true);
                FemaleMapper femaleMapper = sqlSession.getMapper(FemaleMapper.class);
                Female female = femaleMapper.getFemaleById(1);
                System.out.println(female);
                sqlSession.commit();
                System.out.println("-----------分隔符----------------------------------");
                SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
                FemaleMapper femaleMapper2 = sqlSession2.getMapper(FemaleMapper.class);
                Female female2 = femaleMapper2.getFemaleById(1);
                System.out.println(female2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    运行结果:

    哎哎哎,报了个错,记得Female 需要序列化

    Opening JDBC Connection
    Created connection 490150701.
    ==>  Preparing: SELECT id,name FROM tb_female where id = ?; 
    ==> Parameters: 1(Integer)
    <==    Columns: id, name
    <==        Row: 1, soga
    <==      Total: 1
    Female(id=1, name=soga)
    -----------分隔符----------------------------------
    Cache Hit Ratio [qinfeng.zheng.FemaleMapper]: 0.5
    Female(id=1, name=soga)

    请看,这次只查了一个库,第2冲直接缓存命中、

    我们再运行一次刚才的代码

    Cache Hit Ratio [qinfeng.zheng.FemaleMapper]: 1.0
    Female(id=1, name=soga)
    -----------分隔符----------------------------------
    Cache Hit Ratio [qinfeng.zheng.FemaleMapper]: 1.0
    Female(id=1, name=soga)

    两次都缓存命中!!

    再看一下redis:

    好了, mybatis整合redis为二级缓存到此完毕,下章分析一下它的源码实现!

  • 相关阅读:
    个人项目(零)
    个人项目
    H5页面键盘弹出,遮挡输入框问题(转文)
    UEditor 使用setContent()遇到的奇葩问题
    限制textarea每行输入N个字符串
    权限管理简易实现
    使用git上传github遇到的问题
    跟随鼠标移动的遮罩层
    Web字体大揭秘
    @media的使用
  • 原文地址:https://www.cnblogs.com/z-qinfeng/p/11920606.html
Copyright © 2011-2022 走看看