JPA标准谓词条件 [英] JPA Criteria Predicate Conditions

查看:136
本文介绍了JPA标准谓词条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码片段用于构建条件构建器where条件。

I have the following code snippet for building criteria builder where condition.

想知道是否有任何方法可以使这更好,因为我会有更多的条件并且相同的条件将用于获取记录数。

Would like to know are there any ways of making this better as I would have more where conditions and same conditions will be used for getting count of records.

任何见解都非常值得

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


推荐答案

首先,您必须考虑重组您的申请分层的方式。您至少需要3层,DAO,服务和WebService。

First of all you must consider to restructure your application in a layered manner. You at least need 3 layer, DAO, Service and WebService.

关于数据库和JPA的所有内容都必须在DAO层中。所有与json相关的事情都必须在您的WebService层中。您的服务层必须管理事务以及Web服务和dao层之间的通信。

All the things about database and JPA must be in your DAO layer. And all the json related things must be in your WebService layer. Your service layer must manage the transaction and the communication between web service and dao layer.

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

First lets talk about your Web Service layer. Your JSON objects probably come from a Restful web service. Since almost all the frameworks supports json marshalling/unmarshalling it is not wise to parse your data transfer objects manually. By this I mean, you may prefer to declare a FieldDto class and pass its instances around instead of JSONObject. Here is an example of FieldDto. It is a POJO.

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

您可以使用GSON或Jackson轻松地对json进行编组/解组。可能你的框架默认使用其中一个来处理json转换。

You can easily marshall/unmarshall to json using GSON or Jackson. Probably your framework has one of these on default to handle json conversion.

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

Next layer is Service layer. In service layer you manage your transactions and convert your DTO objects to something that your DAO layer can easily understand. In this case your service layer pass fieldDto.getProdId() and fielDto.getProdName() to DAO layer.

您的最后一层是DAO图层。首先,我们可以更改您的方法签名。

Your last layer is DAO layer. First lets change your method signature.

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类时,你会发现一些代码块一遍又一遍地重复。

This is not it. This code still needs improvement. In one of my projects, I create a fluent api to manage all the boilerplate parts. When you start to write other DAO classes you will realise that some of the code blocks repeats over and over again.

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

Here is an example of a fluent api. You may want to construct your version of it.

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

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

If you use this. Than your method become 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来处理它会更好样板代码块并提高可重用性。例如,您需要将之类的语句添加到上面的代码中。

It will be better if you write your own SimpleSelectBuilder to handle boilerplate code blocks and increase reusability. For example you need to add like statement to code above.

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

Managing all the layers, transactions, connection-pools etc will take your time a lot. Instead you may want to consider a middleware to manage all of this for your. In my projects I prefer Spring.

这篇关于JPA标准谓词条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆