spring boot + mybatis 用redis实现mybatis的二级缓存

背景:

最近公司项目中需要引入缓存机制来减轻数据库负载,所以对一些缓存方案进行了研究,其中包括看了几篇讲mybatis的二级缓存的,写的都很不错,推荐美团的一篇:聊聊MyBatis缓存机制 对mybatis的缓存机制讲的很清楚了。博主在本文提供一种使用redis的hash结构来实现mybatis的二级缓存方案,初次尝试,如有疑问欢迎指正。

环境:

  1. jdk 1.8
  2. ide:Intellij 2017.1
  3. spring-boot :2.0.4.RELEASE
  4. redis:4.0.2
  5. mysql:5.7.20

搭建项目&实现

  1. 通过idea新建spring-boot项目,傻瓜式的选择一些需要的starter即可,不再赘述:参考图片

  2. 数据源、redis、mybatis相关配置 (spring-boot的自动配置这里也很简单)直接加入相关的配置即可如下:
    spring:
      redis:
        host: 127.0.0.1
        password:
        port: 6379
        jedis:
          pool:
            min-idle: 0
            max-active: 8
            max-idle: 8
            max-wait: 5000
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/song?characterEncoding=UTF-8&allowMultiQueries=true
        username: root
        password: 123456
        driver-class-name: com.mysql.jdbc.Driver
    mybatis:
      configuration:
        cache-enabled: true #开启全局二级缓存  mybatis通过这个会使用CachingExecutor
      mapper-locations: classpath:mapper/*.xml
    
    
  3. 正常mybatis使用配置(beans、mapper配置、mapper接口):
    1. 定义一个User bean
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
      
          private Integer id;
      
          private String name;
      
          private Integer gender;
      }
      
    2. mapper接口
      @Mapper
      public interface UserMapper {
      
          User selectOne(Integer id);
      
          void insertOne(User user);
      
      }
    3. mapper配置
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.example.demo.mapper.UserMapper">
          <!--使用自定义的缓存
          <cache type="com.example.demo.cache.RedisCache"/>
          -->
          <resultMap id="userResultMap" type="com.example.demo.entities.User">
              <id property="id" column="id" jdbcType="INTEGER" javaType="java.lang.Integer"/>
              <result property="name" column="name" jdbcType="VARCHAR" javaType="java.lang.String"/>
              <result property="gender" column="gender" jdbcType="TINYINT" javaType="java.lang.Integer"/>
          </resultMap>
      
          <select id="selectOne" resultMap="userResultMap" parameterType="java.lang.Integer">
              SELECT id, name, gender
              FROM `USER`
              WHERE id = #{id}
          </select>
      
          <insert id="insertOne" parameterType="com.example.demo.entities.User" useGeneratedKeys="true" keyProperty="id">
              INSERT INTO `USER` (name, gender) VALUES (#{name}, #{gender})
          </insert>
      
      </mapper>

      最好先测试下不使用缓存时代码能用

  4. RedisTemplate的配置:
    @Configuration
    public class RedisConfiguration {
    
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Bean
        public RedisTemplate<String,Object> redisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
            return redisTemplate;
        }
    
        /**
         * 设置数据存入 redis 的序列化方式
         * @param template
         * @param factory
         */
        private void initDomainRedisTemplate(RedisTemplate<String, Object> template, RedisConnectionFactory factory) {
            // 定义 key 的序列化方式为 string
            // 需要注意这里Key使用了 StringRedisSerializer,那么Key只能是String类型的,不能为Long,Integer,否则会报错抛异常。
            StringRedisSerializer redisSerializer = new StringRedisSerializer();
            template.setKeySerializer(redisSerializer);
            // 定义 value 的序列化方式为 json
            @SuppressWarnings({"rawtypes", "unchecked"})
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setValueSerializer(jackson2JsonRedisSerializer);
    
            //hash结构的key和value序列化方式
            template.setHashKeySerializer(jackson2JsonRedisSerializer);
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            template.setEnableTransactionSupport(true);
            template.setConnectionFactory(factory);
        }
    
    }
  5. 自定义实现mybatis的cache接口  直接上代码吧
    
    public class RedisCache implements Cache {
    
        private String id;
    
        private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        public RedisCache(String id) {
            this.id = id;
        }
    
        private RedisTemplate<Object, Object> getRedisTemplate(){
            return ApplicationContextHolder.getBean("redisTemplate");
        }
    
        @Override
        public String getId() {
            return id;
        }
    
        @Override
        public void putObject(Object key, Object value) {
            getRedisTemplate().boundHashOps(getId()).put(key, value);
        }
    
        @Override
        public Object getObject(Object key) {
            return getRedisTemplate().boundHashOps(getId()).get(key);
        }
    
        @Override
        public Object removeObject(Object key) {
            return getRedisTemplate().boundHashOps(getId()).delete(key);
        }
    
        @Override
        public void clear() {
            getRedisTemplate().delete(getId());
        }
    
        @Override
        public int getSize() {
            Long size = getRedisTemplate().boundHashOps(getId()).size();
            return size == null ? 0 : size.intValue();
        }
    
        @Override
        public ReadWriteLock getReadWriteLock() {
            return readWriteLock;
        }
    }

    这里注意RedisTemplate不能通过spring的resource注入,因为cache对象是和id绑定的,可以debug看下这个id其实是mapper中的namespace、这也是为什么自定义cache中必须有一个有参构造器;这个实现中也是根据这个id来为每个namespace对应一个redis的hash结构,在clear时只删掉本namespace的缓存即可

  6. mapper中使用自定义cache,只需要在mapper中加上<cache>标签,具体其中的属性可以自己Google
    <cache type="com.example.demo.cache.RedisCache"/>

所有代码见:https://github.com/swg-liuge/spring-boot-mybatis-cache2-redis

猜你喜欢

转载自blog.csdn.net/weiguang111/article/details/81626679