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?
- 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
- Can better adapt distributed
- 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’