在某些情况下,我们需要从关联的数据模型返回元组行列表,即不是完全限定的实体,而是其中的一部分,特别是来自关联数据源的选定列的列表(可能是数据库)。
我知道一些使用JPA从数据库返回元组行列表的方法,如下所示
如果您不喜欢条件查询,则无需仔细查看JPA条件API中的代码。该问题与JPA标准没有直接关系。我更喜欢JPA标准和JPQL,因为我非常喜欢标准查询。
使用对象数组列表 - List<Object[]>
:
public List<Object[]> object(int first, int pageSize) {
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]>criteriaQuery=criteriaBuilder.createQuery(Object[].class);
Root<Product> root = criteriaQuery.from(entityManager.getMetamodel().entity(Product.class));
List<Selection<?>>selections=new ArrayList<Selection<?>>();
selections.add(root.get(Product_.prodId));
selections.add(root.get(Product_.prodName));
selections.add(root.get(Product_.prodCode));
selections.add(root.get(Product_.prodDesc));
selections.add(root.get(Product_.marketPrice));
selections.add(root.get(Product_.salePrice));
criteriaQuery.select(criteriaBuilder.array(selections.toArray(new Selection[0])));
//Or criteriaQuery.multiselect(selections.toArray(new Selection[0]));
return entityManager.createQuery(criteriaQuery).setFirstResult(first).setMaxResults(pageSize).getResultList();
}
使用元组列表 - List<Tuple>
:
public List<Tuple> tuple(int first, int pageSize) {
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple>criteriaQuery=criteriaBuilder.createTupleQuery();
Root<Product> root = criteriaQuery.from(entityManager.getMetamodel().entity(Product.class));
List<Selection<?>>selections=new ArrayList<Selection<?>>();
selections.add(root.get(Product_.prodId));
selections.add(root.get(Product_.prodName));
selections.add(root.get(Product_.prodCode));
selections.add(root.get(Product_.prodDesc));
selections.add(root.get(Product_.marketPrice));
selections.add(root.get(Product_.salePrice));
criteriaQuery.select(criteriaBuilder.tuple(selections.toArray(new Selection[0])));
//Or criteriaQuery.multiselect(selections.toArray(new Selection[0]));
return entityManager.createQuery(criteriaQuery).setFirstResult(first).setMaxResults(pageSize).getResultList();
}
使用映射一类对象的行列表 - List<MappedClass>
:
public List<ProductUtils> constructor(int first, int pageSize) {
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<ProductUtils>criteriaQuery=criteriaBuilder.createQuery(ProductUtils.class);
Root<Product> root = criteriaQuery.from(entityManager.getMetamodel().entity(Product.class));
List<Selection<?>>selections=new ArrayList<Selection<?>>();
selections.add(root.get(Product_.prodId));
selections.add(root.get(Product_.prodName));
selections.add(root.get(Product_.prodCode));
selections.add(root.get(Product_.prodDesc));
selections.add(root.get(Product_.marketPrice));
selections.add(root.get(Product_.salePrice));
criteriaQuery.select(criteriaBuilder.construct(ProductUtils.class, selections.toArray(new Selection[0])));
//Or criteriaQuery.multiselect(selections.toArray(new Selection[0]));
return entityManager.createQuery(criteriaQuery).setFirstResult(first).setMaxResults(pageSize).getResultList();
}
同样可以使用JPQL重写同样的东西。
前两个是丑陋的,需要在XHTML页面上使用EL中的索引来访问属性。如果字段出现的顺序在以后更改(当然,别名可以与Tuple
一起使用),则难以维护它们。此外,使用Tuple
始终是可以避免的,因为它需要来自javax.persistence
包的JSF中的额外依赖性,从而增加模块之间的耦合。
使用构造函数查询将结果列表映射到类可能就足够了。它可以与PrimeFaces LazyDataModel
一起使用,如下所示。
@Named
@ViewScoped
public class TestManagedBean extends LazyDataModel<ProductUtils> implements Serializable {
@Inject
private Service service;
private static final long serialVersionUID=1L;
public TestManagedBean() {}
@Override
public List<ProductUtils> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) {
// Put some logic here like setting total rows for LazyDataModel - setRowCount(10)
return service.constructor(first, pageSize); //Use filters and sort meta whenever necessary.
}
}
但是这也太难以维护了,如果我需要稍后在不同的地方从数据库访问更多或更少的字段,这需要创建一个新类或添加一个新的构造函数(现有类中的构造函数重载)现有的类反过来需要仔细检查构造函数方法的实际和形式参数,如果它们的数量,顺序和类型匹配,那通常会让我失明。
我希望,应该有一些更好的方法让我们能够以精确的方式解决这些问题。
现有实体类中的参数化构造函数(如果使用的话)(在这种情况下不创建像ProductUtils
这样的新类)可能会在实现Web服务(JAX-WS)时导致问题申请(如果需要)。因此,我从不倾向于在任何地方使用实体类的参数化构造函数。