使用Querydsl的JPA谓词

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

我在我的应用程序中使用Querydsl来大大改善查询数据库的代码,但是,我有一个JPA Predicate (javax.persistence.candards.Predicate),它来自一个外部服务,我想混合我的Querydsl创建的查询和外部的Predicate。例如,我想把我用Querydsl创建的查询和外部谓词混合起来。

// JPA Predicate from external service
Root<User> root = ...;
CriteriaBuilder cb = ...;
javax.persistence.criteria.Predicate externalPredicate = externalService.filterEmail(root, cb, "%@gmail.com");

// Create Querydsl predicate
BooleanExpression querydslExp = QUser.username.eq("foo");

// Mix predicates (this is what I need)
querydslExp.and(externalPredicate);

另一个解决方案是将Querydsl predicate转换成JPA的。

cb.and(querydslExp.toJpaPredicate(), externalPredicate);

如果可以,我如何实现这个目标?如果不可以,是否有其他解决方案来混合谓词(比如将两者转换为SQL字符串并创建一个新的查询)?

如果没有,有没有其他的解决方案来混合谓词(比如将两者转换为SQL字符串并创建新的查询)?

jpa spring-data-jpa querydsl
1个回答
0
投票

使用QueryDSL表达式是可以做到的。Visitor 返回一个JPA Criteria Expression 并使用 CriteriaBuilderCriteriaQuery 作为背景。

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> query = criteriaBuilder.createQuery();

javax.persistence.criteria.Expression<?> result = expression.accept(new Visitor<javax.persistence.criteria.Expression<?>, CriteriaBuilder>() {
    @Override
    public javax.persistence.criteria.Expression<?> visit(Constant<?> expr, CriteriaBuilder context) {
        return context.literal(expr.getConstant());
    }

    @Override
    public javax.persistence.criteria.Expression<?> visit(FactoryExpression<?> expr, CriteriaBuilder context) {
        throw new UnsupportedOperationException();
    }

    @Override
    public javax.persistence.criteria.Expression<?> visit(Operation<?> expr, CriteriaBuilder context) {
        javax.persistence.criteria.Expression<?>[] arguments = new javax.persistence.criteria.Expression<?>[expr.getArgs().size()];
        for (int i = 0; i < expr.getArgs().size(); i++) {
            // Visit arguments recursively
            arguments[i] = expr.getArg(i).accept(this, context);
        }

        if (expr.getOperator() instanceof Ops) {
            switch ((Ops) expr.getOperator()) {
                // For example, add more...
                case EQ:
                    return context.equal(arguments[0], arguments[1]);
                default:
                    // Assuming expr.getOperator().name() as function here is not always enough,
                    // it would be better to use a switch on various operation types.
                    return context.function(expr.getOperator().name(), expr.getType(), arguments);
            }
        }

    }

    @Override
    public javax.persistence.criteria.Expression<?> visit(ParamExpression<?> expr, CriteriaBuilder context) {
        return context.parameter(expr.getType(), expr.getName());
    }

    @Override
    public javax.persistence.criteria.Expression<?> visit(Path<?> expr, CriteriaBuilder context) {
        if (expr.getMetadata().isRoot()) {
            // Get query root
            return query.getRoots().stream().filter(r -> r.getAlias().equals(expr.getMetadata().getName())).findAny().get();
        }

        // Get parent path
        javax.persistence.criteria.Path<?> parent = (javax.persistence.criteria.Path<?>) expr.getMetadata().getParent().accept(this, context);
        return parent.get(expr.getMetadata().getName());
    }

    @Override
    public javax.persistence.criteria.Expression<?> visit(SubQueryExpression<?> expr, CriteriaBuilder context) {
        // Defenitely possible
        throw new UnsupportedOperationException();
    }

    @Override
    public javax.persistence.criteria.Expression<?> visit(TemplateExpression<?> expr, CriteriaBuilder context) {
        // No real JPA equivalent exist. ORM-specifically there are possibilities.
        throw new UnsupportedOperationException();
    }
}, criteriaBuilder);
© www.soinside.com 2019 - 2024. All rights reserved.