JPA & Criteria API-只选择特定的列

我希望只选择特定的列(例如。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<? extends T>) in the type CriteriaQuery<T> is not applicable for the arguments (Path<Object>).我该怎么改变呢?我想得到一个类型 T对象,它只有 IDVERSION字段,其他所有字段都是 null

类型 T扩展了包含这两个字段的 AbstractEntity

entityClazzT.class

145221 次浏览

First of all, I don't really see why you would want an object having only ID and Version, and all other props to be nulls. However, here is some code which will do that for you (which doesn't use JPA Em, but normal Hibernate. I assume you can find the equivalence in JPA or simply obtain the Hibernate Session obj from the em delegate 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();

This will return a list of Objects having their ID and Version set and all other props to null, as the aliasToBean transformer won't be able to find them. Again, I am uncertain I can think of a situation where I would want to do that.

One of the JPA ways for getting only particular columns is to ask for a Tuple object.

In your case you would need to write something like this:

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

Another approach is possible if you have a class representing the result, like T in your case. T doesn't need to be an Entity class. If T has a constructor like:

public T(Long id, Long version)

then you can use T directly in your CriteriaQuery constructor:

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

See this link for further reference.

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

You can do something like this

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

where you can change "firstname" with the name of the column you want.