具有JPA和规范的Spring缓存

问题描述 投票:0回答:1

我使用Spring Boot 2.2,Spring Data REST,Hibernate,Spring Redis创建了REST应用程序。

我配置了Redis服务器,希望在其中缓存一些查询。我已经做了我可以做的所有优化,但这是一个分布式应用程序,我需要使用集中式缓存来帮助提高性能。一切正常,但是当我使用Spring存储库的方法时,我看不到如何创建方便的键

@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);
        }
    }
}

[当我调用Page findAll(Specification specification, Pageable pageable);时,在我的CustomKeyGenerator中我得到了参数,但是我收到了一个SpecificationComposition(这是Spring助手类),而不是Specification。每次我调用该方法时,其哈希码都不同,即使“其内容相同”。

[就像每次都以相同方式对(C0类)进行哈希处理的Pageable一样,我想对PageRequest进行同样的处理,以便获得Spring缓存机制的优势。

Specification

您有什么提示可以告诉我正确的方法吗?

java spring spring-boot spring-data-rest spring-cache
1个回答
1
投票

经过很多努力后,我发布了我的个人解决方案。我不认为这是对的,也不是“最佳实践”,所以请按原样进行。我想分享,因为也许可以帮助有同样需求的人。

这是密钥生成器:

During debug,to show the parameter SpecificationComposition

这是完成任务的服务:

@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();
        }
    }
}

该服务无法处理所有@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; } } / Predicate情况,但会在未处理时发出警告。

[基本思想是从Expression创建一个Predicate并对其进行分析以生成其值的字符串,然后创建一个hashCode。

使用此代码,结果键看上去稳定,现在我可以像Specification一样缓存我的查询了>

© www.soinside.com 2019 - 2024. All rights reserved.