JPA & Criteria API - 仅选择特定列

Posted

技术标签:

【中文标题】JPA & Criteria API - 仅选择特定列【英文标题】:JPA & Criteria API - Select only specific columns 【发布时间】:2012-09-19 01:38:22 【问题描述】:

我只想选择特定的列(例如SELECT a FROM b)。我有一个通用的 DAO,我想出的是:

public List<T> getAll(boolean idAndVersionOnly) 
    CriteriaBuilder builder = manager.getCriteriaBuilder();
    CriteriaQuery<T> criteria = builder.createQuery(entityClazz);
    Root<T> root = criteria.from(entityClazz);
    if (idAndVersionOnly) 
        criteria.select(root.get("ID").get("VERSION")); // HERE IS ERROR
     else 
        criteria.select(root);
    
    return manager.createQuery(criteria).getResultList();

错误是: The method select(Selection&lt;? extends T&gt;) in the type CriteriaQuery&lt;T&gt; is not applicable for the arguments (Path&lt;Object&gt;)。我应该如何改变它?我想得到一个类型为T 的对象,它只有IDVERSION 字段,而其他所有字段都是null

类型 T 扩展 AbstractEntity 具有这两个字段。

entityClazzT.class

【问题讨论】:

【参考方案1】:

仅获取特定列的 JPA 方法之一是请求 Tuple 对象。

在你的情况下,你需要这样写:

CriteriaQuery<Tuple> cq = builder.createTupleQuery();
// write the Root, Path elements as usual
Root<EntityClazz> root = cq.from(EntityClazz.class);
cq.multiselect(root.get(EntityClazz_.ID), root.get(EntityClazz_.VERSION));  //using metamodel
List<Tuple> tupleResult = em.createQuery(cq).getResultList();
for (Tuple t : tupleResult) 
    Long id = (Long) t.get(0);
    Long version = (Long) t.get(1);

如果您有一个代表结果的类,则可以使用另一种方法,例如 T 在您的情况下。 T 不需要是实体类。如果T 有这样的构造函数:

public T(Long id, Long version)

那么你可以直接在你的CriteriaQuery构造函数中使用T

CriteriaQuery<T> cq = builder.createQuery(T.class);
// write the Root, Path elements as usual
Root<EntityClazz> root = cq.from(EntityClazz.class);
cq.multiselect(root.get(EntityClazz_.ID), root.get(EntityClazz_.VERSION));  //using metamodel
List<T> result = em.createQuery(cq).getResultList();

请参阅此link 以获取更多参考。

【讨论】:

【参考方案2】:
cq.select(cb.construct(entityClazz.class, root.get("ID"), root.get("VERSION")));  // HERE IS NO ERROR

https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Criteria#Constructors

【讨论】:

【参考方案3】:

首先,我真的不明白为什么你会想要一个只有 ID 和 Version 的对象,而所有其他道具都是空值。但是,这里有一些代码可以为您做到这一点(它不使用 JPA Em,而是使用普通的 Hibernate。我假设您可以在 JPA 中找到等价物,或者简单地从 em 委托获取 Hibernate Session obj Accessing Hibernate Session from EJB using EntityManager ):

List<T> results = session.createCriteria(entityClazz)
    .setProjection( Projections.projectionList()
        .add( Property.forName("ID") )
        .add( Property.forName("VERSION") )
    )
    .setResultTransformer(Transformers.aliasToBean(entityClazz); 
    .list();

这将返回一个对象列表,其 ID 和版本设置以及所有其他属性为 null,因为 aliasToBean 转换器将无法找到它们。同样,我不确定我是否能想到我想要这样做的情况。

【讨论】:

谢谢。我想在我的网络服务中使用它。客户端请求仅包含 ID 和版本的“某物”列表。然后他将其与缓存进行比较,并请求更改的完整对象。听起来合理吗? 是为了节省网络带宽吗?我认为它取决于数据,但是如果您希望您的数据经常更改,那么由于创建两个请求的 tcp/ip 开销,您可能会浪费更多的带宽,而不是仅仅发送“空心”副本。也许值得一试? 是的,我知道。我只会将它用于不会经常更改的数据。 好吧,我在 JPA 2 中找不到类似的东西:/【参考方案4】:

你可以这样做

Session session = app.factory.openSession();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery query = builder.createQuery();
Root<Users> root = query.from(Users.class);
query.select(root.get("firstname"));
String name = session.createQuery(query).getSingleResult();

您可以在其中将“名字”更改为所需列的名称。

【讨论】:

以上是关于JPA & Criteria API - 仅选择特定列的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JPA Criteria API 上正确使用 JOIN

比较 JPA Criteria API 中的日期实体

使用 JPA 的 Criteria API 按日期间隔分组

jpa criteria-api:加入子选择

Jersey API + JPA/Hibernate Criteria延迟加载不起作用

JPA Criteria Query API 和两列排序