前言
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。Ehcache是一种广泛使用的开 源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
优点:
1. 快速
2. 简单
3. 多种缓存策略
4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
5. 缓存数据会在虚拟机重启的过程中写入磁盘
6. 可以通过RMI、可插入API等方式进行分布式缓存
7. 具有缓存和缓存管理器的侦听接口
8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
9. 提供Hibernate的缓存实现
缺点:
1. 使用磁盘Cache的时候非常占用磁盘空间:这是因为DiskCache的算法简单,该算法简单也导致Cache的效率非常高。它只是对元素直接追加存储。因此搜索元素的时候非常的快。如果使用DiskCache的,在很频繁的应用中,很快磁盘会满。
2. 不能保证数据的安全:当突然kill掉java的时候,可能会产生冲突,EhCache的解决方法是如果文件冲突了,则重建cache。这对于Cache 数据需要保存的时候可能不利。当然,Cache只是简单的加速,而不能保证数据的安全。如果想保证数据的存储安全,可以使用Bekeley DB Java Edition版本。这是个嵌入式数据库。可以确保存储安全和空间的利用率。
EhCache的分布式缓存有传统的RMI,1.5版的JGroups,1.6版的JMS。分布式缓存主要解决集群环境中不同的服务器间的数据的同步问题。
使用Spring的AOP进行整合,可以灵活的对方法的返回结果对象进行缓存。
下面将介绍Spring+EhCache详细实例。
以下需保证ssm项目及redis可用
步骤一 pom.xml配置
<!-- redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- spring-redis实现 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.10.RELEASE</version>
</dependency>
<!-- Ehcache实现,用于参考 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
步骤二 spring-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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>
<!--设置数据池-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空闲数 -->
<property name="maxIdle" value="${redis.maxIdle}"></property>
<!-- 最小空闲数 -->
<property name="minIdle" value="${redis.minIdle}"></property>
<!-- 最大空连接数 -->
<property name="maxTotal" value="${redis.maxTotal}"></property>
<!-- 最大等待时间 -->
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property>
<!-- 返回连接时,检测连接是否成功 -->
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
</bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.password}"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<!-- 使用中间类解决RedisCache.jedisConnectionFactory的静态注入,从而使MyBatis实现第三方缓存 -->
<bean id="redisCacheTransfer" class="com.zbf.common.cache.RedisCacheTransfer">
<property name="jedisConnectionFactory" ref="connectionFactory" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="connectionFactory" >
<!--以下针对各种数据进行序列化方式的选择-->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<!--<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>-->
</bean>
</beans>
该文件主要是增加从而实现缓存:
<!-- 使用中间类解决RedisCache.jedisConnectionFactory的静态注入,从而使MyBatis实现第三方缓存 -->
<bean id="redisCacheTransfer" class="com.zbf.common.cache.RedisCacheTransfer">
<property name="jedisConnectionFactory" ref="connectionFactory" />
</bean>
步骤三 sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局setting配置,根据需要添加 -->
<!-- 配置mapper 由于使用spring和mybatis的整合包进行mapper扫描,这里不需要配置了。 必须遵循:mapper.xml和mapper.java文件同名且在一个目录 -->
<!-- 配置mybatis的缓存,延迟加载等等一系列属性 -->
<settings>
<!-- 全局映射器启用缓存 *主要将此属性设置完成即可 -->
<setting name="cacheEnabled" value="true" />
<!-- 查询时,关闭关联对象即时加载以提高性能 -->
<setting name="lazyLoadingEnabled" value="false" />
<!-- 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 -->
<setting name="aggressiveLazyLoading" value="true" />
</settings>
<!-- 配置别名 -->
<typeAliases>
<!-- 批量扫描别名 -->
<package name="com.zbf.entity" />
</typeAliases>
<!-- <mappers> </mappers> -->
</configuration>
步骤四 RedisCache、RedisCacheTransfer工具类
package com.zbf.common.cache;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Description:
*
* @author 创建者 zbf
* @author 修改者
* @version 2019/2/21 9:29
* @since 2019/2/21 9:29
*/
public class RedisCache implements Cache{
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
private static JedisConnectionFactory jedisConnectionFactory;
private final String id;
/**
* The {@code ReadWriteLock}.
*/
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
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 void clear() {
RedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
connection.flushDb();
connection.flushAll();
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
}
@Override
public String getId() {
return this.id;
}
@Override
public Object getObject(Object key) {
Object result = null;
RedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
result = serializer.deserialize(connection.get(serializer.serialize(key)));
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}
@Override
public ReadWriteLock getReadWriteLock() {
// TODO Auto-generated method stub
return this.readWriteLock;
}
@Override
public int getSize() {
int result = 0;
RedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
result = Integer.valueOf(connection.dbSize().toString());
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}
@Override
public void putObject(Object key, Object value) {
RedisConnection connection = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
connection.set(serializer.serialize(key), serializer.serialize(value));
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
}
@Override
public Object removeObject(Object key) {
RedisConnection connection = null;
Object result = null;
try {
connection = jedisConnectionFactory.getConnection();
RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
result = connection.expire(serializer.serialize(key), 0);
} catch (JedisConnectionException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.close();
}
}
return result;
}
public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.jedisConnectionFactory = jedisConnectionFactory;
}
}
package com.zbf.common.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
/**
* Description:
*
* @author 创建者 zbf
* @author 修改者
* @version 2019/2/21 9:24
* @since 2019/2/21 9:24
*/
public class RedisCacheTransfer {
@Autowired
public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
RedisCache.setJedisConnectionFactory(jedisConnectionFactory);
}
}
步骤五 mapper文件增加cache配置
<mapper namespace="com.zbf.dao.DepartmentDao">
<cache eviction="LRU" type="com.zbf.common.cache.RedisCache" />
<sql id="table_name">
tbl_dept
</sql>
<sql id="select_fields">
dept_id , dept_name, dept_leader
</sql>
<resultMap id="deptResult" type="com.zbf.entity.Department">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<result column="dept_leader" property="deptLeader"></result>
</resultMap>
<select id="selectDeptsByLimitAndOffset" resultMap="deptResult">
SELECT
dept_id as 'deptId', dept_name as 'deptName', dept_leader as 'deptLeader'
FROM
<include refid="table_name"></include>
LIMIT #{offset}, #{limit}
</select>
<update id="updateDeptById" parameterType="com.zbf.entity.Department">
UPDATE
<include refid="table_name"></include>
SET
dept_name = #{department.deptName, jdbcType = VARCHAR},
dept_leader = #{department.deptLeader, jdbcType = VARCHAR}
WHERE
dept_id = #{deptId}
</update>
</mapper>
运行项目再次查询数据后命中缓存
注意:
查询列表实体对象应该实现Serializable。
优秀缓存文章地址:
纯Ehcache:https://www.cnblogs.com/mxmbk/articles/5162813.html
https://blog.csdn.net/kobe_bps/article/details/77744384
https://blog.csdn.net/m0_37499059/article/details/79459900
mybatis缓存:https://www.cnblogs.com/xdp-gacl/p/4270403.html
https://www.cnblogs.com/moongeek/p/7689683.html(一、二级缓存和自定义缓存)