【问题标题】:JPA Criteria Predicate ConditionsJPA 标准谓词条件
【发布时间】:2015-07-29 12:51:26
【问题描述】:

我有以下代码 sn-p 用于构建条件生成器。

想知道有什么方法可以让这变得更好,因为我会有更多条件和相同条件用于获取记录计数。

任何见解都是非常值得赞赏的

private List <Product> getProducts(MultivaluedMap params) throws JSONException {

    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
    CriteriaQuery <Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);

    Root <Product> root = criteriaQuery.from(Product.class);

    List <Predicate> p = new ArrayList <Predicate> ();
    Predicate prodIdPredicate, prodNamePredicate;

    JSONObject inputJSON = new JSONObject(params);

    if (inputJSON.isNull("filter") == false) {
        JSONObject filter = inputJSON.getJSONObject("filter");
        JSONArray filters = filter.getJSONArray("filters");
        for (int i = 0; i < filters.length(); i++) {

            JSONObject j = (JSONObject) filters.get(i);
            if (j.getString("field").equals("prodId")) {
                prodIdPredicate = criteriaBuilder.like(root.get(Product_.prodId), j.getString("value"));
                p.add(prodIdPredicate);
            }

            if (j.getString("field").equals("prodName")) {
                prodNamePredicate = criteriaBuilder.like(root.get(Product_.prodName), j.getString("value"));
                p.add(prodNamePredicate);
            }

        }
    }
    Predicate[] pr = new Predicate[p.size()];
    p.toArray(pr);
    criteriaQuery.where(pr);    

【问题讨论】:

    标签: java json jpa jpa-2.0 criteria-api


    【解决方案1】:

    首先,您必须考虑以分层方式重组您的应用程序。你至少需要 3 层,DAO、Service 和 WebService。

    所有关于数据库和 JPA 的东西都必须在你的 DAO 层中。并且所有与 json 相关的东西都必须在您的 WebService 层中。您的服务层必须管理 Web 服务和 dao 层之间的事务和通信。

    首先让我们谈谈您的 Web 服务层。您的 JSON 对象可能来自 Restful Web 服务。由于几乎所有框架都支持 json 编组/解组,因此手动解析数据传输对象是不明智的。我的意思是,你可能更喜欢声明一个 FieldDto 类并传递它的实例而不是 JSONObject。这是FieldDto 的示例。这是一个 POJO。

    public class FieldDto {
        private String prodId;
        private String prodName;
        // Getters & Setters etc.
    }
    

    您可以使用 GSON 或 Jackson 轻松编组/解组为 json。可能您的框架默认使用其中之一来处理 json 转换。

    下一层是服务层。在服务层中,您管理您的事务并将您的 DTO 对象转换为您的 DAO 层可以轻松理解的东西。在这种情况下,您的服务层将 fieldDto.getProdId()fielDto.getProdName() 传递给 DAO 层。

    您的最后一层是 DAO 层。首先让我们更改您的方法签名。

    public List <Product> getProducts(String prodId, String prodName) {
    
        CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery <Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
    
        Root <Product> root = criteriaQuery.from(Product.class);
    
        List <Predicate> p = new ArrayList <Predicate> ();
    
        if(prodId != null){
             p.add(criteriaBuilder.like(root.get(Product_.prodId),prodId));
        }
    
        if(prodName != null){
             p.add(criteriaBuilder.like(root.get(Product_.prodName), prodName));
        }
    
        if(!p.isEmpty()){
            Predicate[] pr = new Predicate[p.size()];
            p.toArray(pr);
            criteriaQuery.where(pr);    
        }
        return getEntityManager().createQuery(criteriaQuery).getResultList();
    }
    

    这不是。这段代码仍然需要改进。在我的一个项目中,我创建了一个流利的 api 来管理所有样板部分。当您开始编写其他 DAO 类时,您会意识到某些代码块会一遍又一遍地重复。

    这里是一个流畅的 api 示例。您可能想要构建它的版本。

    import javax.persistence.EntityManager;
    import javax.persistence.LockModeType;
    import javax.persistence.PersistenceException;
    import javax.persistence.TypedQuery;
    import javax.persistence.criteria.*;
    import javax.persistence.metamodel.Attribute;
    import javax.persistence.metamodel.CollectionAttribute;
    import javax.persistence.metamodel.PluralAttribute;
    import javax.persistence.metamodel.SingularAttribute;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import java.util.Vector;
    
    public final class SimpleSelectBuilder<E extends Entity> {
    
        private final EntityManager entityManager;
        private final CriteriaBuilder criteriaBuilder;
        private final CriteriaQuery<E> criteriaQuery;
        private final Root<E> root;
        private final Collection<Predicate> predicates;
    
        private Integer first = null;
        private Integer max = null;
        private LockModeType lockModeType = null;
    
        public SimpleSelectBuilder(final EntityManager entityManager, final Class<E> entityClazz) {
            this.entityManager = entityManager;
            this.criteriaBuilder = entityManager.getCriteriaBuilder();
            this.criteriaQuery = this.criteriaBuilder.createQuery(entityClazz);
            this.root = criteriaQuery.from(entityClazz);
            this.predicates = new Vector<>();
        }
    
        public SimpleSelectBuilder<E> and(final Attribute attribute, final Object value) {
            final Expression expression = this.getExpression(attribute, root);
            this.predicates.add(criteriaBuilder.equal(expression, value));
            return this;
        }
    
        public SimpleSelectBuilder<E> andNotIn(final Attribute attribute, final Collection<Object> values) {
            final Expression expression = this.getExpression(attribute, root);
            this.predicates.add(criteriaBuilder.not(expression.in(values)));
            return this;
        }
    
        public SimpleSelectBuilder<E> andIn(final Attribute attribute, final Collection<Object> values) {
            final Expression expression = this.getExpression(attribute, root);
            this.predicates.add(expression.in(values));
            return this;
        }
    
    
        public SimpleSelectBuilder<E> andContains(final Attribute attribute, final Object value) {
    
            final Expression expression = this.getExpression(attribute, root);
            this.predicates.add(criteriaBuilder.isMember(value, expression));
            return this;
        }
    
        public SimpleSelectBuilder<E> orderByAsc(final Attribute attribute) {
            final List<Order> orders = new ArrayList<>();
            if (this.criteriaQuery.getOrderList() != null) {
                orders.addAll(this.criteriaQuery.getOrderList());
            }
            orders.add(criteriaBuilder.asc(this.getExpression(attribute, root)));
            this.criteriaQuery.orderBy(orders.toArray(new Order[orders.size()]));
            return this;
        }
    
        public SimpleSelectBuilder<E> orderByDesc(final Attribute attribute) {
            List<Order> orders = this.criteriaQuery.getOrderList();
            if (orders == null) {
                orders = new ArrayList<>();
            }
            orders.add(criteriaBuilder.desc(this.getExpression(attribute, root)));
            this.criteriaQuery.orderBy(orders.toArray(new Order[orders.size()]));
            return this;
        }
    
        public SimpleSelectBuilder<E> setFirst(Integer first) {
            this.first = first;
            return this;
        }
    
        public SimpleSelectBuilder<E> setMax(Integer max) {
            this.max = max;
            return this;
        }
    
        public SimpleSelectBuilder<E> setLockModeType(LockModeType lockModeType) {
            this.lockModeType = lockModeType;
            return this;
        }
    
        public List<E> getResultList() {
            final TypedQuery<E> query = this.prepareQuery();
    
            if (lockModeType != null) {
                query.setLockMode(lockModeType);
            }
    
            if (first != null) {
                query.setFirstResult(first);
            }
    
            if (max != null) {
                query.setMaxResults(max);
            }
    
            return query.getResultList();
        }
    
        public List<E> getCacheableResultList() {
            final TypedQuery<E> query = this.prepareQuery();
    
            if (lockModeType != null) {
                query.setLockMode(lockModeType);
            }
    
            if (first != null) {
                query.setFirstResult(first);
            }
    
            if (max != null) {
                query.setMaxResults(max);
            }
    
            query.setHint("org.hibernate.cacheable", true);
            query.setHint("org.hibernate.cacheMode", "NORMAL");
            return query.getResultList();
        }
    
        public E getSingleResult() {
            final TypedQuery<E> query = this.prepareQuery();
    
            if (lockModeType != null) {
                query.setLockMode(lockModeType);
            }
    
            return query.getSingleResult();
        }
    
        public E getCacheableSingleResult() {
            final TypedQuery<E> query = this.prepareQuery();
    
            if (lockModeType != null) {
                query.setLockMode(lockModeType);
            }
    
            query.setHint("org.hibernate.cacheable", true);
            query.setHint("org.hibernate.cacheMode", "NORMAL");
            return query.getSingleResult();
        }
    
        private TypedQuery<E> prepareQuery() {
            this.criteriaQuery.where(this.predicates.toArray(new Predicate[this.predicates.size()]));
            return this.entityManager.createQuery(criteriaQuery);
        }
    
        private <T> Expression<T> getExpression(final Attribute attribute, final From<E, T> from) {
            if (attribute instanceof SingularAttribute) {
                SingularAttribute singularAttribute = (SingularAttribute) attribute;
                return from.get(singularAttribute);
            } else if (attribute instanceof PluralAttribute) {
                PluralAttribute pluralAttribute = (PluralAttribute) attribute;
                return from.get(pluralAttribute);
            } else {
                throw new PersistenceException("Attribute type of '" + attribute
                        + "' must be one of [SingularAttribute, PluralAttribute].");
            }
        }
    
        private <T> Join<E, T> getJoinExpression(final Attribute attribute, final From<E, T> from) {
            if (attribute instanceof SingularAttribute) {
                final SingularAttribute singularAttribute = (SingularAttribute) attribute;
                return from.join(singularAttribute);
            } else if (attribute instanceof CollectionAttribute) {
                final CollectionAttribute collectionAttribute = (CollectionAttribute) attribute;
                return from.join(collectionAttribute);
            } else {
                throw new PersistenceException("Attribute type of '" + attribute
                        + "' must be one of [SingularAttribute, PluralAttribute].");
            }
        }
    
        public SimpleSelectBuilder<E> joinAnd(final Attribute attribute, final Object value, final Attribute... joinOn) {
            Join tableJoin = null;
            for (final Attribute join : joinOn) {
                if (tableJoin == null) {
                    tableJoin = this.getJoinExpression(join, root);
                } else {
                    tableJoin = this.getJoinExpression(join, tableJoin);
                }
    
            }
    
            if (tableJoin == null) {
                throw new PersistenceException("SelectBuilder cannot construct your join statement");
            }
    
            final Expression expression = this.getExpression(attribute, tableJoin);
            this.predicates.add(criteriaBuilder.equal(expression, value));
            return this;
        }
    }
    

    如果你使用这个。比你的方法变成这样。

    public List <Product> getProducts(String prodId, String prodName) {
    
        // TODO add like statement to SimpleSelectBuilder
        return new SimpleSelectBuilder<Product>(this.getEntityManager(), Product.class)
               .and(Product_.prodId, prodId))
               .and(Product_.prodName, prodName))
               .getResultList();
    
    }
    

    如果您编写自己的 SimpleSelectBuilder 来处理样板代码块并提高可重用性会更好。例如,您需要在上面的代码中添加like 语句。

    管理所有层、事务、连接池等会花费您很多时间。相反,您可能需要考虑一个中间件来为您管理所有这些。在我的项目中,我更喜欢 Spring。

    【讨论】:

    • 首先感谢您提供详细的解释。我使用的是 EJB,而不是 Spring。是的,我会分成不同的层次。我会分析你的解决方案。赞赏。
    • @bhdrkn 这是一个很棒的解决方案!我复制了您的 SimpleSelectBuilder 并添加了大约 6 到 8 种其他方法(“like”、“include”(又名 join)、“or”、“subEntityPropertyEquals”(这样我就可以通过其子属性和一堆其他人)。我正在从 C# 转换到 Java,并使用这个流畅的构建器类,我创建了一些感觉像 LINQ 的东西,希望它能帮助我和我的团队进行过渡!再次感谢。跨度>
    猜你喜欢
    • 2011-03-23
    • 2019-12-07
    • 2017-04-08
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 2019-08-26
    • 2019-03-03
    • 1970-01-01
    相关资源
    最近更新 更多