The integration of Spring 4 (including MVC, context, ORM) + MyBatis 3 Example briefly describes the latest version of the spring MVC, IOC, MyBatis ORM Integration, and declarative transaction processing. Now we need to integrate the cache, and we're using Redis, and this article will cover the integration of Redis cache + Spring on the basis of this example. For the construction of Redis server, please refer to the blog "Redhat5.8 Environment" to compile and install Redis and register it as a system service.
1. Dependent Package Installation
Pom.xml join:
<!-- redis cache related.....start -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
<!-- redis cache related.....end -->
2. Spring project integrated into cache support
To enable cache support, we need to create a new CacheManager bean. CacheManager interface has a lot of implementations, this article demonstrates the integration with Redis, is naturally used Rediscachemanager. Redis is not an app's shared memory, it's just a memory server, like MYSQL, we need to connect the app to it and interact with some kind of "language," so we also need a connection factory and a redistemplate for the Spring and Redis conversations, These are the necessary configurations for the Redis cache and put them all in a custom cachingconfigurersupport:
/**
* File Name:RedisCacheConfig.java
*
* Copyright Defonds Corporation 2015
* All Rights Reserved
*
*/
package com.defonds.bdp.cache.redis;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
/**
*
* Project Name:bdp
* Type Name:RedisCacheConfig
* Type Description:
* Author:Defonds
* Create Date:2015-09-21
*
* @version
*
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
// Defaults
redisConnectionFactory.setHostName("192.168.1.166");
redisConnectionFactory.setPort(6379);
return redisConnectionFactory;
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(3000); // Sets the default expire time (in seconds)
return cacheManager;
}
}
Of course, don't forget to inject these beans into Spring, otherwise the configuration will be invalid. Add the following to the Applicationcontext.xml:
<context:component-scan base-package= "Com.defonds.bdp.cache.redis"/>
3. Caching execution results for some methods
After setting up the cache configuration, we can use @Cacheable annotations to cache the results of the method execution, such as retrieving the city's Provincecities method based on the province name and retrieving the city's Searchcity method based on City_code:
// R
@Cacheable("provinceCities")
public List<City> provinceCities(String province) {
logger.debug("province=" + province);
return this.cityMapper.provinceCities(province);
}
// R
@Cacheable("searchCity")
public City searchCity(String city_code){
logger.debug("city_code=" + city_code);
return this.cityMapper.searchCity(city_code);
}
4. Cache Data Consistency Assurance
CRUD (create creation, Retrieve read, update updates, delete delete) operations, in addition to the power of R, the other three can cause cache results and database inconsistencies. In order to ensure the consistency of the cached data, we need to update or clear the cache that may be affected when the CUD operation is performed.
// C
@CacheEvict(value = { "provinceCities"}, allEntries = true)
public void insertCity(String city_code, String city_jb,
String province_code, String city_name,
String city, String province) {
City cityBean = new City();
cityBean.setCityCode(city_code);
cityBean.setCityJb(city_jb);
cityBean.setProvinceCode(province_code);
cityBean.setCityName(city_name);
cityBean.setCity(city);
cityBean.setProvince(province);
this.cityMapper.insertCity(cityBean);
}
// U
@CacheEvict(value = { "provinceCities", "searchCity" }, allEntries = true)
public int renameCity(String city_code, String city_name) {
City city = new City();
city.setCityCode(city_code);
city.setCityName(city_name);
this.cityMapper.renameCity(city);
return 1;
}
// D
@CacheEvict(value = { "provinceCities", "searchCity" }, allEntries = true)
public int deleteCity(String city_code) {
this.cityMapper.deleteCity(city_code);
return 1;
}
Business considerations, this example uses @CacheEvict to clear the cache. If your CUD can return to the city instance, you can also use the @CachePut to update the cache policy. I recommend that you use @CachePut where you do not use @CacheEvict, because the latter will all the relevant methods of the cache is cleared, such as any of the above three methods are called, the Provincecities method of all the cache will be cleared.
5. Custom cache Data Key generation policy
For a method that uses @Cacheable annotations, the key generation policy for each cache uses the parameter name + parameter value by default, such as the following methods:
@Cacheable("users")
public User findByUsername(String username)
The cache for this method will be stored in the cache with key Users~keys, and for username with the value "Zhao Defang", the key is "username-Zhao Defang". In general, there is no problem, the second case, if the method key value is equal and the parameter name is the same when the problem occurs, such as:
@Cacheable("users")
public Integer getLoginCountByUsername(String username)
The cache for this method will also be stored in the cache of key Users~keys. For a cache with a value of "Zhao Defang" for username, key is also "username-Zhao Defang", overwriting the cache of another method.
The workaround is to use a custom cache policy, for the same business (the same business logic processing method, even the cluster/distributed system), the generated key always consistent, for different business is inconsistent:
@Bean
public KeyGenerator customKeyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
So the above two methods, for the username value of "Zhao Defang" cache, although are still stored in the key is Users~keys cache, but because the key is "class name-findbyusername-username-Zhao Defang" and "Class name-getlo gincountbyusername-username-Zhao Defang ", so there's no problem.
This is important for sharing caches between clustered systems and distributed systems, and it really implements distributed caching.
The author suggests that the @Cacheable of the caching method is best to use the method name, avoid the @Cacheable values of the different methods consistent, and then with the above cache policy.
6. Validation of Cached 6.1 cache
In order to determine whether each cache method actually has a cache, we have opened the SQL log output of MyBatis, and we have emptied the Redis database for testing to demonstrate clearly.
To verify the Provincecities method cache, Eclipse starts the Tomcat loading project and uses JMeter to invoke the/bdp/city/province/cities.json interface:
The Eclipse console output is as follows:
Indicates that the request did not hit the cache, and the DB query was gone. JMeter request again, Eclipse console output:
Red section The following is the log for this request, no log to access the DB, and a cache hit. To view Redis storage for this request:
It is also possible to verify that the cache for the Searchcity method with City_code 1492 is valid:
The red part of the figure is the cache storage of searchcity.
6.2 Validation of Cache consistency
To verify the cache configuration of the Insertcity method first, JMeter calls the/bdp/city/create.json interface:
Then look at Redis storage:
It can be seen that the cache of the Provincecities method has been cleaned out and the cache of the Insertcity method works.
Then verify the cache configuration of the Renamecity method, JMeter calls the/bdp/city/rename.json interface:
Then look at Redis storage:
The cache of the Searchcity method has also been cleaned up, and the caching of the Renamecity method has worked.
7. Precautions
- The Java object to be cached must implement the Serializable interface, because Spring will serialize the object first into Redis, such as the Com.defonds.bdp.city.bean.City class in this article, if the Serializable is not implemented You will encounter something like this: Nested exception is java.lang.IllegalArgumentException:DefaultSerializer requires a Serializable payload But received an object of type [com.defonds.bdp.city.bean.City]].
- The life cycle of the cache we can configure and then host Spring CacheManager, and do not attempt to manage the cache through the REDIS-CLI command line. For example, the Provincecities method Cache, a province of the query results will be stored in the form of key-value in Redis,key is our custom generated Key,value is the serialized object, this key will be placed in the key named Provinc Ecities~keys Key-value Storage, refer to the "Provincecities method in Redis cache condition". Provincecities~keys can be deleted by REDIS-CLI using the Del command, but the cache for each province is not cleared.
- CacheManager must set the cache expiration time, otherwise the cached object will never expire, so for the reasons above, avoid some wild data "forever save". In addition, setting the cache expiration time also helps maximize resource utilization, because the cache is always kept hot data.
- The cache is suitable for applications where there is less read and write, the cache hit rate is very low, the write operation is very frequent, and the scenario is not suitable for caching.
Postscript
This article complete Eclipse of the development project example has uploaded CSDN resources, interested friends can go to download down reference: http://download.csdn.net/detail/defonds/9137505.
Resources
- Caching Data with Spring
- Cache Abstraction part VII. Integration
- Caching Data in Spring Using Redis
- Caching with Spring Data Redis
- Spring-redis-caching-example
http://blog.csdn.net/defonds/article/details/48716161
Redis cache + Spring Integration Example (RPM)