current position:Home>Mybatis integrates ehcache and redis to realize secondary cache

Mybatis integrates ehcache and redis to realize secondary cache

2022-01-26 23:44:51 Encounter in the evening wind

In this paper mybatis And spring Integrate Develop and realize on the basis of Mybatis Second level cache .

1.Mybatis Integrate ehcache

1.1pom rely on

In the original pom Add on dependency ehcache Related dependencies

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>

<!--mybatis And ehcache Integrate -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>

<!--ehcache rely on -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.0</version>
</dependency>

Modify log configuration , because ehcache Used Slf4j Output as log , Log we use slf4j, And use log4j To achieve . 

<!-- log4j2 Log configuration related dependencies  -->
    <log4j2.version>2.9.1</log4j2.version>
    <log4j2.disruptor.version>3.2.0</log4j2.disruptor.version>
    <slf4j.version>1.7.13</slf4j.version>

<!-- log4j2 Log related dependencies  -->
    <!-- log To configure :Log4j2 + Slf4j -->
    <!-- slf4j Core packages -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>${slf4j.version}</version>
      <scope>runtime</scope>
    </dependency>

    <!-- The core log4j2jar package -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    <!-- Used with slf4j Keep bridging -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    <!--web The project needs to include log4j-web, Not web The project does not need -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-web</artifactId>
      <version>${log4j2.version}</version>
      <scope>runtime</scope>
    </dependency>

    <!-- Need to use log4j2 Of AsyncLogger Need to include disruptor-->
    <dependency>
      <groupId>com.lmax</groupId>
      <artifactId>disruptor</artifactId>
      <version>${log4j2.disruptor.version}</version>
    </dependency>

1.2ehcache Related configuration files

ehcache.xml The configuration file

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!-- Disk storage : Will cache objects that are temporarily not used , Transfer to hard disk , Be similar to Windows Virtual memory of the system -->
    <!--path: Specifies the path where objects are stored on the hard disk -->
    <!--java.io.tmpdir  Is the default temporary file path .  The specific file path can be printed as follows  System.out.println(System.getProperty("java.io.tmpdir"));-->
    <diskStore path="java.io.tmpdir"/>


    <!--defaultCache: Default management strategy -->
    <!--eternal: Set cached elements Whether it will never expire . If true, The cached data is always valid , If false Then according to timeToIdleSeconds,timeToLiveSeconds Judge -->
    <!--maxElementsInMemory: Cached in memory element The maximum number of -->
    <!--overflowToDisk: If the data in memory exceeds the memory limit , Do you want to cache to disk -->
    <!--diskPersistent: Whether to persist on disk . Restart jvm after , Is the data valid . The default is false-->
    <!--timeToIdleSeconds: Object free time ( Company : second ), It refers to how long an object will be invalid if it is not accessed . Only right eternal by false Effective . The default value is 0, Indicates that you can always access -->
    <!--timeToLiveSeconds: Object lifetime ( Company : second ), It refers to the time required for an object from creation to expiration . Only right eternal by false Effective . The default value is 0, Indicates that you can always access -->
    <!--memoryStoreEvictionPolicy: The cache 3  Kind of emptying strategy -->
    <!--FIFO:first in first out ( fifo )-->
    <!--LFU:Less Frequently Used ( Use at least ). It means the least used . The cached element has a hit  attribute ,hit  The smallest value will be flushed out of the cache -->
    <!--LRU:Least Recently Used( Recently at least use ). (ehcache  The default value is ). Cached elements have a timestamp , When the cache capacity is full , And when you need to make room to cache new elements , The element whose timestamp is farthest from the current time among the existing cache elements will be flushed out of the cache -->
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>

    <!--name: Cache The name of , Must be unique (ehcache Will take this. cache Put it in HashMap in )-->
    <!--<cache name="stuCache" eternal="false" maxElementsInMemory="100"-->
    <!--overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"-->
    <!--timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/>-->
</ehcache>

applicationContext.ehcache.xml The configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  Use ehcache cache  -->
    <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"/>
        <property name="shared" value="true"></property>
    </bean>
    <!--  The default is cacheManager -->
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="cacheManagerFactory"/>
    </bean>
</beans>

 mybaits The secondary cache of is mapper Scope level , In addition to setting the master switch of L2 cache , But also in the specific mapper.xml Enable L2 cache in

applicationContext-mybatis.xml In profile

stay sqlSessionFactory Add cache support

<!-- Set up mybaits Support for caching -->
    <property name="configurationProperties">
        <props>
            <!--  Global mapper enable caching  * Mainly set this property -->
            <prop key="cacheEnabled">true</prop>
            <!--  When inquiring , Turn off immediate loading of associated objects to improve performance  -->
            <prop key="lazyLoadingEnabled">false</prop>
            <!--  Set the loading form of the associated object , Here is the on-demand field ( The loading field consists of SQL finger   set ), All fields of the associated table will not be loaded , To improve performance  -->
            <prop key="aggressiveLazyLoading">true</prop>
        </props>
    </property>

In the corresponding ...Mapper.xml Middle configuration cache

<cache type="org.mybatis.caches.ehcache.LoggingEhcache" />

Summary :Mybatis Once the cache switch is turned on , Can cache a single record , You can also cache multiple ,hibernate Cannot cache multiple ,

  Mybatis Can pass select Labeled useCache Property to turn L2 cache on or off .

2.Mybatis Integrate redis 

2.1pom rely on

add to redis Related dependencies  

 <!-- redis And spring The integration dependence of  -->
    <redis.version>2.9.0</redis.version>
    <redis.spring.version>1.7.1.RELEASE</redis.spring.version>
    
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>${redis.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>${redis.spring.version}</version>
    </dependency>

jackson Related dependencies

<!-- jackson -->
    <jackson.version>2.9.3</jackson.version> 

<!-- jackson -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>

1.2redis Related configuration files  

redis.properties

redis.hostName=192.168.111.111
redis.port=6379
redis.password=123456
redis.timeout=10000
redis.maxIdle=300
redis.maxTotal=1000
redis.maxWaitMillis=1000
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=1024
redis.timeBetweenEvictionRunsMillis=30000
redis.testOnBorrow=true
redis.testWhileIdle=true

 applicationContext-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1.  introduce properties The configuration file  -->
    <!--<context:property-placeholder location="classpath:redis.properties" />-->

    <!-- 2. redis Connection pool configuration -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- Maximum free number -->
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <!-- Maximum number of database connections to the connection pool   -->
        <property name="maxTotal" value="${redis.maxTotal}"/>
        <!-- Maximum connection setup wait time -->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        <!-- Minimum idle time to evict connections   Default 1800000 millisecond (30 minute )-->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
        <!-- Every time you eject it   The maximum number of evictions   If it's negative, it's  : 1/abs(n),  Default 3-->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
        <!-- The interval between evictions ( millisecond )  If it's negative , The eviction thread is not run ,  Default -1-->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
        <!-- Whether to check before removing the connection from the pool , If the test fails , Remove the connection from the pool and try to remove another -->
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        <!-- Check validity in your spare time ,  Default false  -->
        <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
    </bean>

    <!-- 3. redis Connection factory  -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          destroy-method="destroy">
        <property name="poolConfig" ref="poolConfig"/>
        <!--IP Address  -->
        <property name="hostName" value="${redis.hostName}"/>
        <!-- Port number   -->
        <property name="port" value="${redis.port}"/>
        <!-- If Redis Set up a password   -->
        <property name="password" value="${redis.password}"/>
        <!-- The client timeout is in milliseconds   -->
        <property name="timeout" value="${redis.timeout}"/>
    </bean>

    <!-- 4. redis Operating templates , Use this object to manipulate redis  -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <!-- If you don't configure Serializer, Then, it is used by default when storing String, If you use User Type storage , Then it will prompt an error User can't cast to String!!  -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <!-- Open transaction   -->
        <property name="enableTransactionSupport" value="true"/>
    </bean>

    <!-- 5. Use intermediate classes to solve RedisCache.RedisTemplate Static injection , So that MyBatis Implement third-party caching  -->
    <bean id="redisCacheTransfer" class="com.javaxl.ssm.util.RedisCacheTransfer">
        <property name="redisTemplate" ref="redisTemplate"/>
    </bean>
</beans>

stay spring Only one is allowed in <context:property-placeholder/>   

applicationContext.xml ( General profile )

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- Multi data source import configuration -->
        <context:property-placeholder location="classpath:jdbc.properties,classpath:redis.properties"/>
        <import resource="applicationContext-mybatis.xml"></import>
                <!--ehcache To configure -->
        <!--<import resource="applicationContext.ehcache.xml"></import>-->
        <import resource="applicationContext-redis.xml"></import>
</beans>

take jdbc as well as redis Put the file in the general configuration file

1.3 take redis Cache is introduced into mybatis in

ehcache Realized mybatis Provided Cache Interface ,  all mybatis The default is to use ehcache. When we need to use redis As a cache , We need to achieve Cache Interface .

RedisCache 

package com.wyy.Util;


import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class RedisCache implements Cache // Implementation class 
{
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    private static RedisTemplate<String,Object> redisTemplate;

    private final String id;

    /**
     * The {@code ReadWriteLock}.
     */
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

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

    public static void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisCache.redisTemplate = redisTemplate;
    }

    public RedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        logger.debug("MybatisRedisCache:id=" + id);
        this.id = id;
    }

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

    @Override
    public void putObject(Object key, Object value) {
        try{
            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>putObject: key="+key+",value="+value);
            if(null!=value)
                redisTemplate.opsForValue().set(key.toString(),value,60, TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("redis Save data exception !");
        }
    }

    @Override
    public Object getObject(Object key) {
        try{
            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>getObject: key="+key);
            if(null!=key)
                return redisTemplate.opsForValue().get(key.toString());
        }catch (Exception e){
            e.printStackTrace();
            logger.error("redis Get data exception !");
        }
        return null;
    }

    @Override
    public Object removeObject(Object key) {
        try{
            if(null!=key)
                return redisTemplate.expire(key.toString(),1,TimeUnit.DAYS);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("redis Get data exception !");
        }
        return null;
    }

    @Override
    public void clear() {
        Long size=redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                Long size = redisConnection.dbSize();
                // Connect and clear data 
                redisConnection.flushDb();
                redisConnection.flushAll();
                return size;
            }
        });
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>clear:  wipe out " + size + " Objects ");
    }

    @Override
    public int getSize() {
        Long size = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.dbSize();
            }
        });
        return size.intValue();
    }
}

At this time in RedisCache Medium redisTemplate yes null. All we need to give redisTemplate assignment

RedisCacheTransfer 

package com.wyy.Util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

public class RedisCacheTransfer {
    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisCache.setRedisTemplate(redisTemplate);
    }
}

stay RedisCacheTransfer There is only one method in this class, which is to RedisCache Medium redisTemplate assignment

technological process : First, in the applicationContext-redis.xml Configuration of the redisTemplate, Re inject into RedisCacheTransfer Medium redisTemplate, Finally, assign a value to RedisCache Medium redisTemplate.

applicationContext-mybatis.xml In profile

stay sqlSessionFactory Add cache support

<!-- Set up mybaits Support for caching -->
    <property name="configurationProperties">
        <props>
            <!--  Global mapper enable caching  * Mainly set this property -->
            <prop key="cacheEnabled">true</prop>
            <!--  When inquiring , Turn off immediate loading of associated objects to improve performance  -->
            <prop key="lazyLoadingEnabled">false</prop>
            <!--  Set the loading form of the associated object , Here is the on-demand field ( The loading field consists of SQL finger   set ), All fields of the associated table will not be loaded , To improve performance  -->
            <prop key="aggressiveLazyLoading">true</prop>
        </props>
    </property>

In the corresponding ...Mapper.xml Configure custom cache

<cache type="com.wyy.Util.RedisCache" />

 

A little skill every day. You've learned it today !!!

copyright notice
author[Encounter in the evening wind],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201262344483164.html

Random recommended