欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

spring boot 2.1.4 休眠 二级缓存 Caffeine 实现

最编程 2024-05-26 12:54:06
...
开源中国社区团队直播首秀,以分享为名讲述开源中国社区背后的故事”

The Ehcache second-level cache provider for Hibernate is deprecated

当我们使用hibernate-ehcache包(Ehcache 2)作为hibernate二级缓存时,系统会提示警告说已经过时了,那这时候找到spring boot推荐的新的二级缓存方案,现在推荐hibernate-jcache,可以与Ehcache 3或是其他实现了javax.cache.spi.CachingProvider的缓存自动集成

jcache是一种缓存门面规范,并不包含具体缓存实现,spring boot推荐与jcache搭配使用的是Hazelcast,Hazelcast实现了CachingProvider,可以直接作为hibernate二级缓存,Hazelcast实现下一篇再提供

hibernate二级缓存重构之后,要自己实现也非常简单,只需要实现

org.hibernate.cache.spi.support.RegionFactoryTemplate

org.hibernate.cache.spi.support.DomainDataStorageAccess

这两个类就可以了,引入caffeine包

<dependency>
	<groupId>com.github.ben-manes.caffeine</groupId>
	<artifactId>caffeine</artifactId>
</dependency>

下面是DomainDataStorageAccess实现,这个类就是缓存操作的实现

import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import lombok.NonNull;

public class CaffeineDataRegion implements DomainDataStorageAccess {
	protected final Logger log = LoggerFactory.getLogger(this.getClass());
	/**
	 * Region regionName
	 */
	private final String regionName;
	private final Cache<Object, Object> cache;
	private final int expiryInSeconds; // seconds
	static final int DEFAULT_EXPIRY_IN_SECONDS = 1800;

	public CaffeineDataRegion(@NonNull String regionName) {
		this.regionName = StringUtils.replace(regionName, ".", ":") + ":";
		this.expiryInSeconds = DEFAULT_EXPIRY_IN_SECONDS;
		cache = Caffeine.newBuilder()
				// 设置cache中的数据在写入之后的存活时间
				.expireAfterWrite(30, TimeUnit.MINUTES)
				// 构建cache实例
				.build();
		log.debug("caffeiene region={}, expiryInSeconds={}", regionName, expiryInSeconds);
	}

	/**
	 * confirm the specified key exists in current region
	 *
	 * @param key
	 *            cache key
	 * @return if cache key is exists in current region return true, else return
	 *         false
	 */
	@Override
	public boolean contains(Object key) {
		try {
			log.debug("contains key={}", key);
			return cache.getIfPresent(key) != null;
		} catch (Exception ignored) {
			log.warn("Fail to exists key. key=" + key, ignored);
			return false;
		}
	}

	@Override
	public Object getFromCache(Object key, SharedSessionContractImplementor session) {
		try {
			return cache.getIfPresent(key);
		} catch (Exception ignored) {
			log.warn("Fail to get cache item... key=" + key, ignored);
			return null;
		}
	}

	@Override
	public void putIntoCache(Object key, Object value, SharedSessionContractImplementor session) {
		try {
			cache.put(key, value);
		} catch (Exception ignored) {
			log.warn("Fail to put cache item... key=" + key, ignored);
		}
	}

	@Override
	public void evictData() {
		try {
			cache.invalidateAll();
		} catch (Exception ignored) {
			log.warn("Fail to clear region... name=" + regionName, ignored);
		}
	}

	@Override
	public void evictData(Object key) {
		try {
			cache.invalidate(key);
		} catch (Exception ignored) {
			log.warn("Fail to remove cache item... key=" + key, ignored);
		}
	}

	@Override
	public void release() {

	}
}

下面是RegionFactoryTemplate实现,这个类是缓存启动类

import java.util.Map;

import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
import org.hibernate.cache.spi.support.StorageAccess;
import org.hibernate.engine.spi.SessionFactoryImplementor;

import com.bc.plugin.caffeine.hibernate.regions.CaffeineDataRegion;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CaffeineRegionFactory extends RegionFactoryTemplate {
	private static final long serialVersionUID = 1L;

	@Override
	protected StorageAccess createQueryResultsRegionStorageAccess(String regionName,
			SessionFactoryImplementor sessionFactory) {
		return new CaffeineDataRegion(regionName);
	}

	@Override
	protected StorageAccess createTimestampsRegionStorageAccess(String regionName,
			SessionFactoryImplementor sessionFactory) {
		return new CaffeineDataRegion(regionName);
	}

	@Override
	protected DomainDataStorageAccess createDomainDataStorageAccess(DomainDataRegionConfig regionConfig,
			DomainDataRegionBuildingContext buildingContext) {
		return new CaffeineDataRegion(regionConfig.getRegionName());
	}

	@Override
	protected void prepareForUse(SessionFactoryOptions settings, @SuppressWarnings("rawtypes") Map configValues) {
		log.debug("RegionFactory is starting... options={}, properties={}", settings, configValues);
	}

	@Override
	protected void releaseFromUse() {

	}
}

 

然后配置spring.jpa.properties.hibernate.cache.region.factory_class=/*org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory  */

org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory是hibernate-ehcache包中的实现,

替换成我们CaffeineRegionFactory类的全路径就可以了

引入caffeine包后,spring cache也会使用caffeine,springboot会自动配置caffeine

启用二级缓存配置

spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
spring.jpa.properties.hibernate.javax.cache.missing_cache_strategy=create
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.use_second_level_cache=true

 

修正:

caffeine提供了jcache规范的实现 

直接引入caffeine jcache包就可以了,上述方式有点多余

        <!-- 集成jcache规范 -->
		<dependency>
			<groupId>javax.cache</groupId>
			<artifactId>cache-api</artifactId>
		</dependency>
		<!-- 集成hibernate jcache规范 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-jcache</artifactId>
		</dependency>
		<dependency>
			<groupId>com.github.ben-manes.caffeine</groupId>
			<artifactId>jcache</artifactId>
		</dependency>

配置项修改为:

spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.internal.JCacheRegionFactory

使用jcache提供的JCacheRegionFactory就可以了