【问题标题】:Is there a way to reduce the amount of boiler-plate code associated with a CriteriaQuery (in JPA 2.0)?有没有办法减少与 CriteriaQuery 相关的样板代码量(在 JPA 2.0 中)?
【发布时间】:2011-03-03 12:32:26
【问题描述】:

我喜欢类型安全CriteriaQuery 带来了 JPA 2.0,但它也带来了一些样板代码。例如,假设我有一个名为 NamedEntity 的实体,它只有一个 id 和一个名为“name”的字符串字段(假设它的唯一约束设置为 true)。 NamedEntityManager 可能如下所示:

public class NamedEntityManager
{
    //inject using your framework
    EntityManager entityManager;

    //retrieve all existing entities of type NamedEntity from DB
    public Iterable<NamedEntity> queryAll()
    {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
        return entityManager.createQuery(query).getResultList();
    }

    //retrieve a single entity of type NamedEntity from DB using specified name
    public NamedEntity queryByName(String name)
    {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
        Root<NamedEntity> root = query.from(NamedEntity.class);
        query = query.where(root.<NamedEntity>get("name").in(name));

        //skipped the try/catch block for the sake of brevity
        return entityManager.createQuery(query).getSingleResult();
    }
}

有没有办法压缩代码以避免将相同的代码行复制/粘贴到每个查询方法中?也许以某种方式重用 CriteriaQuery 对象?

【问题讨论】:

  • 这种情况可以使用策略轻松解决。只需创建一个私有方法,该方法应采用一个类型的参数(接口),例如 WhereClauseBuilder,私有方法将通过将条件构建器和查询传递给它的方法调用从该参数获取其变化部分(where 子句)。所有公共方法,将只调用具有特定 WhereClauseBuilder 的私有方法,该方法返回所需的谓词 where 子句。

标签: java jpa jpa-2.0 criteriaquery


【解决方案1】:

我正在寻找类似的东西,你可以看看Querydsl(LGPL 许可),它可以有 JPA 作为后端。

我还在读它,但从他们的例子来看,它看起来很干净。

HQLQuery q = new HibernateQuery(session);
QCat cat = new QCat("cat"); // query type
List<Cat> cats = q.from(cat).where(cat.name.between("A", "B")).list(cat);

【讨论】:

  • 虽然这个框架与 Criteria API 无关并且独立存在,但它当然值得研究。感谢您提及这一点,有机会我会探索它!
  • 只是一个小修正,Querydsl 是 LGPL 许可的,而不是 GPL 许可的。
  • querydsl.com 似乎许可证已更改为 Apache 许可证,版本 2.0。
【解决方案2】:

在 JPA 2.1 中,很可能混合使用 JPQL 和 Criterias。使用这种方法,您可以使用 JPQL 定义基本查询,然后使用 Criteria API 动态添加小部分。

我认为 API 会不那么冗长,因为您只需要使用它的一小部分。

【讨论】:

  • 我不确定将 JPQL 与 Criteria Queries 混合是否有意义。包含 JPQL 将违背 Criteria API 背后的主要思想 - 提供编译时类型安全。
  • 这当然是一种权衡。就像使用 EL 和在某些情况下使用注释一样。您正在用编译时安全性来换取灵活性和更少冗长的代码。无论如何,很多人确实认为这是有道理的,因为 Linda(规范负责人)正在认真考虑 JPA 2.1 的这一点。那么选择权在您手中:纯 JPQL、JPQL 与 Criteria 混合,甚至在 Criterias 中您也有类型安全和非类型安全的变体。使用适合您的。
【解决方案3】:

【讨论】:

  • 使用元模型并没有减少那么多代码量,但它确实提高了清晰度并进一步增加了类型安全性。
【解决方案4】:

似乎没有办法减少代码量。我想必须牺牲一些东西来获得类型安全。

【讨论】:

    【解决方案5】:

    这篇文章已经过时了,但我想添加我最近为简单查询构建的内容

        public static class Jpa2Whatsoever {
    
        private final EntityManager em;
    
        public class Jpa2WhatsoeverProgress<T> {
    
            private CriteriaQuery<T> cq;
            private List<Predicate> predicates = new ArrayList<>();
            private Root<T> root;
    
            public Jpa2WhatsoeverProgress(Class<T> type) {
                this.cq = em.getCriteriaBuilder().createQuery(type);
                this.root = cq.from(type);
    
            }
    
            public Jpa2WhatsoeverProgress<T> where(String attributeName, Object value) {
    
                Predicate equal = em.getCriteriaBuilder().equal(root.get(attributeName), value);
    
                predicates.add(equal);
                return this;
            }
    
            public List<T> getResultList() {
                Predicate[] predicatesArray = new Predicate[predicates.size()];
                TypedQuery<T> typedQuery = em.createQuery(cq.select(root).where(predicates.toArray(predicatesArray)));
    
                List<T> resultList = typedQuery.getResultList();
    
                return Collections.unmodifiableList(resultList);
            }
    
        }
    
        public Jpa2Whatsoever(EntityManager entityManager) {
            this.em = entityManager;
        }
    
        public <T> Jpa2WhatsoeverProgress<T> select(Class<T> type) {
            return new Jpa2WhatsoeverProgress<T>(type);
        }
    }
    

    你可以这样使用它

    List<MyEntity> matchingEntities = new Jpa2Whatsoever(entityManager).select(MyEntity.class).where("id", id).where("due", new Date()).getResultList();
    

    最后我停止了。主要是因为我看到我只有两个查询,我必须扩展 DSL 才能将所需的查询特征放入其中,例如

    • 大于,小于
    • 元模型支持
    • QueryBuilder.currentDate() 等。

    此外,我发现总是调用 where 很难看,而实际上它对应于更 SQLly 的 and。无论如何,如果有人对一个非常简单的查询 API 感兴趣,还是值得一试的。

    顺便说一句:忘记名字吧,这是一个原型,仅此而已。

    【讨论】:

      猜你喜欢
      • 2013-07-07
      • 1970-01-01
      • 2010-12-07
      • 2015-01-03
      • 2019-02-16
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 2012-06-18
      相关资源
      最近更新 更多