mybatis redis implemented using two cache (spring boot)

mybatis redis do custom secondary cache

Foreword

If the focus function to achieve, you can achieve some functionality directly see

When using the secondary cache

A purpose --- rarely becomes stable and commonly used

  • Sqlsession level is the default level of open.
  • Used only in a single table, and all operations are under a namespace
  • In the case of small additions and deletions to multi-query
  • Cache is not all advantages and disadvantages are obvious, sometimes the cache is not the latest data.

    Secondary cache Parameter Description

    This is effective when a cross Sqlsession level cache, though, is the mapper level, that is, you can access the same mapper multiple sqlsession

Keyword Reading
eviction Cache recovery strategy
flushInterval Refresh interval, in milliseconds
size Reference number, how many objects can be stored on behalf of the cache up to
readOnly The default read-only if false True if all the sql returns an object, high concurrency safety performance at the end, if false returns the copy serialized, high efficiency end security

Common recovery strategy (with the operating system memory recovery strategy almost)

  • LRU default, the least recently used
  • FIFO First In First Out
  • SOFT soft references remove the reference to rules based on the garbage collector state and soft objects
  • WEAK weak references more aggressive removal of an object reference rule-based garbage collector state and remove weak

    sql control whether to use cache and cache refresh

<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/> 
<select id="" resultMap="" useCache="false">
<update id="" parameterType="" flushCache="false" />

When the cache will fail (meaning that constitute the key factors of the same, but did not hit value)

A failure

  • Not in the same Sqlsession, for example, is not on the transaction, mybatis each query will shut down the old to create a new one.
  • CRUD operation program will clear the cache
  • Execution sqlsession.clearCache Manual ()

    Two failures

  • Perform insert, update, delete statement
  • time out
  • Recycling is recovered strategy

Function to achieve

Add dependent

<!-- 集成了lettuce的连接方式也可以用jedis方式看自己建议用集成的说明稳定些 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- 序列化 -->
        <dependency>
            <groupId>de.javakaffee</groupId>
            <artifactId>kryo-serializers</artifactId>
        </dependency>
        <!-- 连接池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!-- 自定义获取Bean -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <!-- 断言判断,正式环境中可以使用 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

Start secondary cache

  • You can configure the call mapper project, to facilitate future maintenance
spring: 
  redis:
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
#集群的方式
#    sentinel:
#      master: mymaster
#      nodes: 192.168.15.154:6379
    database: 0
    host: 192.168.15.154
    port: 6379
# MyBatis Config properties
mybatis:
  type-aliases-package: 
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    cache-enabled: true

Customize relevant code

  • Make sure that all POJO have achieved serialized and the serial number of the statement
  private static final long serialVersionUID =-1L;
  • Custom implementation cache interface
/**
 * @author lyy
 * @description
 * @date 2019/9/2
 */
public class RedisCache implements Cache {
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // cache instance id
    private final String id; 
    private RedisTemplate redisTemplate;
    // redis过期时间
    private static final long EXPIRE_TIME_IN_MINUTES = 30; 

    public RedisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        this.id = id;
    }

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

    /**
     * Put query result to redis
     *
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        try {
            RedisTemplate redisTemplate = getRedisTemplate();
            ValueOperations opsForValue = redisTemplate.opsForValue();
            opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
            logger.debug("Put query result to redis");
        } catch (Throwable t) {
            logger.error("Redis put failed", t);
        }
    }

    /**
     * Get cached query result from redis
     *
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        try {
            RedisTemplate redisTemplate = getRedisTemplate();
            ValueOperations opsForValue = redisTemplate.opsForValue();
            logger.debug("Get cached query result from redis");
            return opsForValue.get(key);
        } catch (Throwable t) {
            logger.error("Redis get failed, fail over to db", t);
            return null;
        }
    }

    /**
     * Remove cached query result from redis
     *
     * @param key
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object removeObject(Object key) {
        try {
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.delete(key);
            logger.debug("Remove cached query result from redis");
        } catch (Throwable t) {
            logger.error("Redis remove failed", t);
        }
        return null;
    }

    /**
     * Clears this cache instance
     */
    @Override
    public void clear() {
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.execute((RedisCallback) connection -> {
            connection.flushDb();
            return null;
        });
        logger.debug("Clear all the cached query result from redis");
    }

    /**
     * This method is not used
     *
     * @return
     */
    @Override
    public int getSize() {
        return 0;
    }

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

    private RedisTemplate getRedisTemplate() {
        if (redisTemplate == null) {
            redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
        }
        return redisTemplate;
    }
}
  • Defined ApplicationContextHolder to achieve inject Bean and access

/**
 * @author lyy
 * @description
 * @date 2019/9/2
 */
@Component
public class ApplicationContextHolder implements ApplicationContextAware, DisposableBean {
    private static final Logger logger = LoggerFactory.getLogger(ApplicationContextHolder.class);

    private static ApplicationContext applicationContext;


    public static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException(
                    "'applicationContext' property is null,ApplicationContextHolder not yet init.");
        }
        return applicationContext;
    }

    /**
     *      * 根据bean的id来查找对象
     *      * @param id
     *      * @return
     *      
     */
    public static Object getBeanById(String id) {
        checkApplicationContext();
        return applicationContext.getBean(id);
    }

    /**
     *      * 通过名称获取bean
     *      * @param name
     *      * @return
     *      
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        checkApplicationContext();
        Object object = applicationContext.getBean(name);
        return (T) object;
    }

    /**
     *      * 根据bean的class来查找对象
     *      * @param c
     *      * @return
     *      
     */
    @SuppressWarnings("all")
    public static <T> T getBeanByClass(Class<T> c) {
        checkApplicationContext();
        return (T) applicationContext.getBean(c);
    }

    /**
     *      * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * 如果有多个Bean符合Class, 取出第一个.
     *      * @param cluss
     *      * @return
     *      
     */
    public static <T> T getBean(Class<T> cluss) {
        checkApplicationContext();
        return (T) applicationContext.getBean(cluss);
    }

    /**
     *      * 名称和所需的类型获取bean
     *      * @param name
     *      * @param cluss
     *      * @return
     *      
     */
    public static <T> T getBean(String name, Class<T> cluss) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name, cluss);
    }

    public static <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        checkApplicationContext();
        return applicationContext.getBeansOfType(type);
    }

    /**
     * 检查ApplicationContext不为空.
     */
    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义ApplicationContextHolderGm");
        }
    }

    @Override
    public void destroy() throws Exception {
        checkApplicationContext();
    }

    /**
     * 清除applicationContext静态变量
     */
    public static void cleanApplicationContext() {
        applicationContext = null;
    }


    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        //checkApplicationContext();
        applicationContext = context;
        logger.info("holded applicationContext,显示名称:" + applicationContext.getDisplayName());
    }
}

Enable Cache

  • Notes the way, annotation on an interface mapper
@CacheNamespace(implementation = RedisCache.class)
  • xml way
 <cache type="***.RedisCache">
    <property name="eviction" value="LRU"/>
    <property name="flushInterval" value="60000000"/>
    <property name="size" value="2048"/>
    <property name="readOnly" value="false"/>
  </cache>

Why redis? What else besides redis other?

  1. redis nosql library is a high performance, accompanied by a cluster stable and efficient, since the default is in the secondary cache memory. This allows the cache independent, third-party map structure but also all the advantages of doing secondary cache
  2. Can better adapt distributed
  3. There ehcache, as well as in theory, all libraries should be able to nosql

Precautions

Cache does not take effect (or called cache penetration)

  • Sql annotation using annotations to open, to open sql xml configuration in xml, the mix does not take effect
  • redis version of windows you want to configure it to allow external network connections, even in the bind 127.0.0.1 redis.windows.conf commented out does not work
# Examples:
#
# bind 192.168.1.100 10.0.0.1
bind 0.0.0.0
  • intellij idea generated sequence number, put the cursor under the selected category is, the interface not press the alt + enter
File -> Settings -> Inspections -> Serialization issues -> Serialization class without ‘serialVersionUID’

Guess you like

Origin www.cnblogs.com/ants_double/p/11484435.html