Introduction:
1. Why cache
Pull up program performance
2. What kind of data needs to be cached
Data that is rarely modified or not modified at all
Business scenarios, such as time-consuming statistical analysis sql, telephone bill query sql, etc
3. What is ehcache
Ehcache is the most popular pure Java open source caching framework, with simple configuration, clear structure and powerful functions
Note 1: this chapter introduces 2 Version x, 3 X version and 2 The API versions of X are quite different
4. Features of ehcache
4.1 fast enough
Ehcache has been released for a long time. After years of efforts and countless performance tests, ehcache was finally designed in large, high concurrency systems
4.2 simple enough
The interface provided by developers is very simple and clear. It only takes you a few precious minutes from ehcache construction to application and operation. In fact, many developers do not know they are using ehcache. Ehcache is widely used in other open source projects
4.3 pocket size
For this feature, the official gave a lovely name small foot print. Generally, the release version of Ehcache will not reach 2M, V 2.2 3 is only 668KB.
4.4 light enough
The core program only depends on slf4j this package, not one!
4.5 good expansion
Ehcache provides memory and hard disk storage for big data. The latest version allows multiple instances, high flexibility of saving objects, LRU, LFU and FIFO elimination algorithms, and basic attributes support hot configuration and multiple plug-ins
4.6 listener
It's very useful for the cache manager listener and cache evenlistener to broadcast statistics or data consistency
4.7 distributed cache
Starting from Ehcache 1.2, it supports high-performance distributed cache with flexibility and scalability
3. Use of ehcache
3.1 import related dependencies
3.2 core interface
CacheManager: cache manager
Cache: cache object. Several caches can be placed in the cache manager to store the essence of data. All caches implement the Ehcache interface
Element: the constituent unit of a single piece of cached data
I. mybatis uses Ehcache as the L2 cache
Import POM XML dependency:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!--mybatis And ehcache integration--> <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:
The log uses slf4j and is implemented with log4j. Slf4j is very different from other log class libraries.
slf4j(Simple logging Facade for Java) is not a real logging implementation, but an abstraction layer,
It allows you to use any log class library in the background.
slf4j is an abstract logging framework. Its advantage is that it has no specific implementation. Who will implement it depends on the framework itself
Replace with log4j2 log configuration related dependencies
<!-- 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>
Replace with log4j2 log related dependencies
<!-- log4j2 Log dependent dependencies --> <!-- log to configure: Log4j2 + Slf4j --> <!-- slf4j Core package--> <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> <!--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,wrong web The project does not require--> <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>
Add ehcache xml:
<?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:Temporarily unused objects in the cache,Transfer to hard disk,be similar to Windows Virtual memory of the system--> <!--path:Specifies the path to store objects on the hard disk--> <!--java.io.tmpdir Is the default temporary file path. You can print out the specific file path in the following ways System.out.println(System.getProperty("java.io.tmpdir"));--> <diskStore path="java.io.tmpdir"/> <!--defaultCache: Default management policy--> <!--eternal: Set cached elements Whether to never expire. If yes true,The cached data is always valid if false Then according to timeToIdleSeconds,timeToLiveSeconds judge--> <!--maxElementsInMemory: Cached in memory element Maximum number of--> <!--overflowToDisk: If the data in memory exceeds the memory limit, do you want to cache it to disk--> <!--diskPersistent: Whether to persist on disk. Means restart jvm Whether the data is valid after. Default to false--> <!--timeToIdleSeconds: Object idle time(Unit: Second),It refers to how long an object will become invalid if it is not accessed. Only right eternal by false The is valid. The default value is 0, which means it can always be accessed--> <!--timeToLiveSeconds: Object lifetime(Unit: Second),It refers to the time required for an object from creation to expiration. Only right eternal by false The is valid. The default value is 0, which means it can always be accessed--> <!--memoryStoreEvictionPolicy: Three cache emptying strategies--> <!--FIFO: first in first out (fifo)--> <!--LFU: Less Frequently Used (Minimum use).It means the least used all the time. Cached element has a hit Properties, hit The smallest value will be flushed out of the cache--> <!--LRU: Least Recently Used(Least recently used). (ehcache Default value).The cached element has a timestamp. When the cache capacity is full and it needs to make room for caching new elements, the element with the farthest timestamp from the current time in the existing cache element will be cleared out of the cache--> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/> <!--name: Cache The name of the must be unique(ehcache I'll take this cache put to HashMap in)--> <!--<cache name="stuCache" eternal="false" maxElementsInMemory="100"--> <!--overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"--> <!--timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/>--> </ehcache>
In ApplicationContext Adding chache configuration to XML
Copy ApplicationContext mybatis Create applicationContext.xml XML file
Put ApplicationContext XML as the total number of spring files
<?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"> <import resource="classpath:applicationContext-mybatis.xml"></import> <import resource="classpath:applicationContext-ehcache.xml"></import> </beans>
Create ApplicationContext ehcache xml
It stores ehcache XML cache configuration, so that ehcache can be managed by sparing
<?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>
The second level cache of mybaits is the mapper range level, except in sqlmapconfig XML to set the master switch of L2 cache, and also in the specific mapper Enable L2 cache in XML
Enable the L2 cache of mybatis:
applicationContext-mybatis. Add to XML
<!--set up mybaits Cache support--> <property name="configurationProperties"> <props> <!-- Global mapper enable caching *Mainly set this property--> <prop key="cacheEnabled">true</prop> <!-- When querying, turn off immediate loading of associated objects to improve performance --> <prop key="lazyLoadingEnabled">false</prop> <!-- Set the loading mode of associated objects. Here is the on-demand loading field(Loading fields by SQL appoint),All fields of the associated table are not loaded to improve performance --> <prop key="aggressiveLazyLoading">true</prop> </props> </property>
BookMapper. Add L2 cache core class to XML
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
Execution results:
The L2 cache is used successfully, and the query statement appears only once:
The mybatis framework caches multiple pieces of data by default:
The L2 cache can be turned on or off through the useCache attribute of the select tag
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" useCache="false"></select>
be careful:
The second level cache framework used by mybatis by default is ehcache (org.mybatis.caches.ehcache.EhcacheCache), which combines seamlessly
Once the Mybatis cache switch is turned on, you can cache a single record or multiple records. hibernate cannot cache multiple records.
All methods on the Mapper interface additionally provide the property to close the cache
Second direct access to cache:
All queries will enter the cache the second time
Querying multiple pieces of data into L2 cache is not allowed because it will reduce performance
useCache="false"
2, mybatis uses redis as the L2 cache
1. Common redis classes
1.1 Jedis
jedis integrates some command operations of redis and encapsulates the java client of redis
1.2 JedisPoolConfig
Redis connection pool
1.3 ShardedJedis
Distributed Redis cluster client based on consistent hash algorithm
Generally speaking, there are two ways to implement the secondary cache of mybatis:
(1) adopt the built-in cache mechanism of mybatis.
(2) adopt the third-party cache framework, such as ehcache, oscache, etc
First, import the dependency of Redis:
<!-- redis And spring Integration dependency --> <redis.version>2.9.0</redis.version> <redis.spring.version>1.7.1.RELEASE</redis.spring.version> Note: don't put it in the wrong position!!! <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>
Open rdm software and connect to Redis:
Import jackson related configuration (for serialization in Redis):
<!-- jackson --> <jackson.version>2.9.3</jackson.version> Note: not all copy,Pay attention to import dependencies separately!!! <!-- 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>
spring + redis integration enables caching (not related to mybatis)
First, establish Redis Properties configuration of non relational database (be sure to modify the host name of your Redis connection)
redis.hostName=(own ip (address) redis.port=6379 redis.password=(Own password) 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 configuration file --> <!--<context:property-placeholder location="classpath:redis.properties" />--> <!-- 2. redis Connection pool configuration--> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--Maximum idle--> <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}"/> <!--The minimum idle time for evicting connections is 1800000 milliseconds by default(30 minute)--> <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/> <!--The maximum number of evictions per eviction check is negative : 1/abs(n), Default 3--> <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/> <!--Time interval of eviction scan(millisecond) If negative,The eviction thread is not run, default-1--> <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/> <!--Are connections verified before they are removed from the pool,If the inspection fails,Then remove the connection from the pool and try to fetch another one--> <property name="testOnBorrow" value="${redis.testOnBorrow}"/> <!--Check validity when idle, 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 password --> <property name="password" value="${redis.password}"/> <!--The client timeout is in milliseconds --> <property name="timeout" value="${redis.timeout}"/> </bean> <!-- 4. redis Operation template,Use this object to manipulate redis --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="connectionFactory"/> <!--If not configured Serializer,It is used by default when storing String,If used User Type storage, an error will be prompted 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.Using intermediate classes to solve RedisCache.RedisTemplate Static injection, so that MyBatis Implement third-party caching --> <bean id="redisCacheTransfer" class="com.lsy.util.RedisCacheTransfer"> <property name="redisTemplate" ref="redisTemplate"/> </bean> </beans>
applicationContext.xml: firstly, annotate the original scattered data sources, and configure the multi data source import configuration in the general file entry
<context:property-placeholder location="classpath:jdbc.properties,classpath:redis.properties"/>
Create mybatis's custom cache class "RedisCache.java", which must implement org apache. ibatis. cache. Cache interface
RedisCache.java:
package com.dzl.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(); //Connection 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(); } }
Statically inject the intermediate class "RedisCacheTransfer.java" to solve the static injection of RedisTemplate in RedisCache, so that MyBatis can realize the third-party cache
RedisCacheTransfer.java:
package com.dzl.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); } }
Test:
@Test public void testCacheOne() { System.out.println(this.bookService.selectByPrimaryKey(38)); System.out.println(this.bookService.selectByPrimaryKey(38)); }
Here, the test passed and the data entered: