Java 具有JPA和规范的Spring缓存
我使用SpringBoot2.2、SpringDataREST、Hibernate和SpringRedis创建了一个REST应用程序 我配置了一个Redis服务器,我想在其中缓存一些查询。我已经做了所有可以做的优化,但这是一个分布式应用程序,我需要通过集中式缓存稍微提高性能。 一切正常,但我不知道如何在使用Spring存储库的方法时创建一个方便的键Java 具有JPA和规范的Spring缓存,java,spring,spring-boot,spring-data-rest,spring-cache,Java,Spring,Spring Boot,Spring Data Rest,Spring Cache,我使用SpringBoot2.2、SpringDataREST、Hibernate和SpringRedis创建了一个REST应用程序 我配置了一个Redis服务器,我想在其中缓存一些查询。我已经做了所有可以做的优化,但这是一个分布式应用程序,我需要通过集中式缓存稍微提高性能。 一切正常,但我不知道如何在使用Spring存储库的方法时创建一个方便的键 @Cacheable(cacheNames = "contacts") @Override Page findAll(Specification s
@Cacheable(cacheNames = "contacts")
@Override
Page findAll(Specification specification, Pageable pageable);
这是我的Redis配置:
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport implements CachingConfigurer {
@Override
public CacheErrorHandler errorHandler() {
return new RedisCacheErrorHandler();
}
@Override
@Bean("customKeyGenerator")
public KeyGenerator keyGenerator() {
return new CustomKeyGenerator();
}
}
和我的密钥生成器:
public class CustomKeyGenerator implements KeyGenerator {
public CustomKeyGenerator() {
}
@Override
public Object generate(Object target, Method method, Object... params) {
List<Object> listParams = new ArrayList<>(Arrays.asList(params));
listParams.add(TenantContext.getCurrentTenantId());//Add tenantId as parameter
if (StoreContext.getCurrentStoreId() != null)
listParams.add(StoreContext.getCurrentStoreId());//Add storeId as parameter
return generateKey(listParams.toArray());
}
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
} else {
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
}
}
@Log4j2
public class CustomKeyGenerator implements KeyGenerator {
private static SpecificationService specificationService;
public CustomKeyGenerator() {
specificationService = SpringBeansLoadUtils.getBean(SpecificationService.class);
}
@Override
public Object generate(Object target, Method method, Object... params) {
//********************************************************************
// GET GENERICS IN CASE
// For methods like Page<Document> findAll(Specification specification, Pageable pageable);
// get the Generics type needed to
//********************************************************************
Class returnClass = method.getReturnType();
Class realClass = null;
if (Collection.class.isAssignableFrom(returnClass) || Page.class.isAssignableFrom(returnClass)) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) returnType;
Type[] argTypes = paramType.getActualTypeArguments();
if (argTypes.length > 0) {
realClass = (Class) argTypes[0];
}
}
}
List<Object> listParams = new ArrayList<>(Arrays.asList(params));
listParams.add(TenantContext.getCurrentTenantId());//Add tenantId as parameter
if (StoreContext.getCurrentStoreId() != null)
listParams.add(StoreContext.getCurrentStoreId());//Add storeId as parameter
return generateKey(realClass, listParams.toArray());
}
public static Object generateKey(Class clazz, Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
} else {
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
HashCodeBuilder builder = new HashCodeBuilder();
for (Object p : params) {
if (p != null && p.getClass().getSimpleName().contains("SpecificationComposition")) {
builder.append(specificationService.hashCode(clazz, (Specification) p));
} else {
builder.append(Arrays.deepHashCode(new Object[]{p}));
}
}
log.info("Hash {}", builder.hashCode());
return builder.hashCode();
}
}
}
公共类CustomKeyGenerator实现KeyGenerator{
公钥生成器(){
}
@凌驾
公共对象生成(对象目标、方法、对象…参数){
List listParams=newarraylist(Arrays.asList(params));
listParams.add(TenantContext.getCurrentTenantId());//将tenantId添加为参数
if(StoreContext.getCurrentStoreId()!=null)
listParams.add(StoreContext.getCurrentStoreId());//将storeId添加为参数
返回generateKey(listParams.toArray());
}
公共静态对象generateKey(对象…参数){
如果(params.length==0){
返回SimpleKey.EMPTY;
}否则{
如果(params.length==1){
对象参数=参数[0];
if(param!=null&&!param.getClass().isArray()){
返回参数;
}
}
返回新的SimpleKey(params);
}
}
}
当我调用Page findAll(规范规范,可分页)
,在我的CustomKeyGenerator
中,我获得了参数,但我收到了一个SpecificationComposition
(它是一个Spring助手类)来代替Specification
。每次我调用该方法时,它的哈希代码都是不同的,即使“它的内容是相同的”
与每次都以相同方式散列的Pageable(PageRequest
class)一样,我也希望对规范
进行同样的操作,以获得Spring缓存机制的优势
你有什么提示告诉我正确的方法吗?经过很多努力,我发布了我的个人解决方案。我不会假装这是正确的或“最佳实践”,所以请接受它。我想与大家分享,因为,也许,我可以帮助有同样需要的人 这是密钥生成器:
public class CustomKeyGenerator implements KeyGenerator {
public CustomKeyGenerator() {
}
@Override
public Object generate(Object target, Method method, Object... params) {
List<Object> listParams = new ArrayList<>(Arrays.asList(params));
listParams.add(TenantContext.getCurrentTenantId());//Add tenantId as parameter
if (StoreContext.getCurrentStoreId() != null)
listParams.add(StoreContext.getCurrentStoreId());//Add storeId as parameter
return generateKey(listParams.toArray());
}
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
} else {
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
}
}
@Log4j2
public class CustomKeyGenerator implements KeyGenerator {
private static SpecificationService specificationService;
public CustomKeyGenerator() {
specificationService = SpringBeansLoadUtils.getBean(SpecificationService.class);
}
@Override
public Object generate(Object target, Method method, Object... params) {
//********************************************************************
// GET GENERICS IN CASE
// For methods like Page<Document> findAll(Specification specification, Pageable pageable);
// get the Generics type needed to
//********************************************************************
Class returnClass = method.getReturnType();
Class realClass = null;
if (Collection.class.isAssignableFrom(returnClass) || Page.class.isAssignableFrom(returnClass)) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) returnType;
Type[] argTypes = paramType.getActualTypeArguments();
if (argTypes.length > 0) {
realClass = (Class) argTypes[0];
}
}
}
List<Object> listParams = new ArrayList<>(Arrays.asList(params));
listParams.add(TenantContext.getCurrentTenantId());//Add tenantId as parameter
if (StoreContext.getCurrentStoreId() != null)
listParams.add(StoreContext.getCurrentStoreId());//Add storeId as parameter
return generateKey(realClass, listParams.toArray());
}
public static Object generateKey(Class clazz, Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
} else {
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
HashCodeBuilder builder = new HashCodeBuilder();
for (Object p : params) {
if (p != null && p.getClass().getSimpleName().contains("SpecificationComposition")) {
builder.append(specificationService.hashCode(clazz, (Specification) p));
} else {
builder.append(Arrays.deepHashCode(new Object[]{p}));
}
}
log.info("Hash {}", builder.hashCode());
return builder.hashCode();
}
}
}
@Log4j2
公共类CustomKeyGenerator实现KeyGenerator{
专用静态规范服务规范服务;
公钥生成器(){
specificationService=SpringBeansLoadUtils.getBean(specificationService.class);
}
@凌驾
公共对象生成(对象目标、方法、对象…参数){
//********************************************************************
//获取泛型以备不时之需
//对于PageFindall(规格说明,可分页);
//获取所需的泛型类型
//********************************************************************
Class returnClass=method.getReturnType();
类realClass=null;
if(Collection.class.isAssignableFrom(returnClass)| | Page.class.isAssignableFrom(returnClass)){
类型returnType=method.getGenericReturnType();
if(返回类型instanceof ParameterizedType){
ParameteredType paramType=(ParameteredType)returnType;
类型[]argTypes=paramType.getActualTypeArguments();
如果(argTypes.length>0){
realClass=(Class)argTypes[0];
}
}
}
List listParams=newarraylist(Arrays.asList(params));
listParams.add(TenantContext.getCurrentTenantId());//将tenantId添加为参数
if(StoreContext.getCurrentStoreId()!=null)
listParams.add(StoreContext.getCurrentStoreId());//将storeId添加为参数
返回generateKey(realClass,listParams.toArray());
}
公共静态对象generateKey(类clazz、对象…参数){
如果(params.length==0){
返回SimpleKey.EMPTY;
}否则{
如果(params.length==1){
对象参数=参数[0];
if(param!=null&&!param.getClass().isArray()){
返回参数;
}
}
HashCodeBuilder=新的HashCodeBuilder();
用于(对象p:params){
如果(p!=null&&p.getClass().getSimpleName()包含(“SpecificationComposition”)){
builder.append(specificationService.hashCode(clazz,(Specification)p));
}否则{
append(Arrays.deepHashCode(新对象[]{p}));
}
}
log.info(“Hash{}”,builder.hashCode());
返回builder.hashCode();
}
}
}
这就是完成这项工作的服务:
@Service
@Transactional
@PreAuthorize("isAuthenticated()")
@Log4j2
public class SpecificationService {
@PersistenceContext(unitName = "optixPU")
private EntityManager entityManager;
/**
* Generate an hashCode of the given specification
*
* @param clazz
* @param spec
* @return
*/
public Integer hashCode(Class clazz, @Nullable Specification spec) {
try {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery query = builder.createQuery(clazz);
Root root = query.from(clazz);
Predicate predicate = spec.toPredicate(root, query, builder);
String hash = analyzePredicate(predicate);
return hash.hashCode();
} catch (Exception e) {
log.warn("", e);
}
return null;
}
private String analyzePredicate(Predicate predicate) {
String stringRepresentation = "";
for (Expression<Boolean> e : predicate.getExpressions()) {
if (e instanceof CompoundPredicate) {
stringRepresentation += analyzePredicate((CompoundPredicate) e);
} else {
if (e instanceof InPredicate) {
InPredicate inPredicate = (InPredicate) e;
for (Object ex : inPredicate.getValues()) {
stringRepresentation += analyzeExpression((Expression) ex);
}
} else if (e instanceof LikePredicate) {
LikePredicate likePredicate = (LikePredicate) e;
String hashExpression = analyzeExpression(likePredicate.getMatchExpression());
String hashPattern = analyzeExpression(likePredicate.getPattern());
stringRepresentation += hashExpression + hashPattern;
} else if (e instanceof ComparisonPredicate) {
String operator = ((ComparisonPredicate) e).getComparisonOperator().toString();
String leftHand = analyzeExpression(((ComparisonPredicate) e).getLeftHandOperand());
String rightHand = analyzeExpression(((ComparisonPredicate) e).getRightHandOperand());
stringRepresentation += operator + leftHand + rightHand;
} else {
log.warn("Predicate not identified: {}", e);
}
}
}
return stringRepresentation;
}
private String analyzeExpression(Expression expression) {
if (expression instanceof SingularAttributePath) {
SingularAttributePath singularAttributePath = (SingularAttributePath) expression;
return singularAttributePath.getAttribute().getName();
} else if (expression instanceof LikeExpression) {
LiteralExpression likeExpression = (LiteralExpression) expression;
return likeExpression.getLiteral().toString();
}
if (expression instanceof LiteralExpression) {
return ((LiteralExpression) expression).getLiteral().toString();
} else if (expression instanceof ConcatExpression) {
ConcatExpression concatExpression = (ConcatExpression) expression;
String code1 = analyzeExpression(concatExpression.getString1());
String code2 = analyzeExpression(concatExpression.getString2());
return code1 + code2;
} else {
log.warn("Expression {} not identified", expression);
}
return null;
}
}
@服务
@交易的
@预授权(“isAuthenticated()”)
@Log4j2
公共类规范服务{
@PersistenceContext(unitName=“optixPU”)
私人实体管理者实体管理者;
/**
*生成给定规范的哈希代码
*
*@param-clazz
*@param规范
*@返回
*/
公共整数哈希码(类clazz,@Nullable规范spec){
试一试{
CriteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery=builder.createQuery(clazz);
Root=query.from(clazz);
谓词=spec.toPredicate(根、查询、生成器);
字符串哈希=分析谓词(谓词);
返回hash.hashCode();
}捕获(例外e){
log.warn(“,e);
}
返回null;
}
私有字符串分析谓词(谓词谓词){
字符串stringRepresentation=“”;
for(表达式e:predicate.getExpre