[英]How to use JPA projections with one-to-many attributes in Criteria API / JPQL
我很难使用Criteria API创建查询,该API 用于投影查询实体的属性并实例化DTO。 其中一个投影属性将一对多关系映射到另一个实体,因此它是一组依赖实体 。 我正在使用fetch join来检索集合。 但是我收到以下错误:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
我已经尝试过使用常规连接,但在这种情况下,将不会填充依赖实体集。 完全删除联接和/或获取也没有帮助。
我正在使用JPA规范2.0,Hibernate 4.2.21.Final,Spring Data JPA 1.10.11.RELEASE。
有人可以就此提出建议吗? 我也很高兴工作的JPQL。
这是我对查询的实现:
@Override
public List<EntityADto> findByPartialKey1OrderByPartialKey2(String partialKey1) {
// Create query
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<EntityADto> criteriaQuery = criteriaBuilder.createQuery(EntityADto.class);
// Define FROM clause
final Root<EntityA> root = criteriaQuery.from(EntityA.class);
root.fetch(EntityA_.oneToManyAttribute);
// Define DTO projection
criteriaQuery
.select(criteriaBuilder.construct(
EntityADto.class,
root.get(EntityA_.id).get(EntityAId_.partialKey1),
root.get(EntityA_.id).get(EntityAId_.partialKey2),
root.get(EntityA_.stringAttribute1),
root.get(EntityA_.stringAttribute2),
root.get(EntityA_.oneToManyAttribute)))
.orderBy(criteriaBuilder.asc(root.get(EntityA_.id).get(EntityAId_.partialKey2)))
.distinct(true);
// Define WHERE clause
final ParameterExpression<String> parameterPartialKey1 = criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(root.get(EntityA_.id).get(EntityAId_.partialKey1), parameterPartialKey1));
// Execute query
final TypedQuery<EntityADto> typedQuery = entityManager.createQuery(criteriaQuery);
typedQuery.setParameter(parameterPartialKey1, partialKey1);
return typedQuery.getResultList();
}
实体看起来如下:
@Entity
@Table(name = "TABLE_A", uniqueConstraints =
@UniqueConstraint(columnNames = {
"PARTIAL_KEY_1", "STRING_ATTR_1", "STRING_ATTR_2" }))
public class EntityA {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "partialKey1", column = @Column(name = "PARTIAL_KEY_1", nullable = false)),
@AttributeOverride(name = "partialKey2", column = @Column(name = "PARTIAL_KEY_2", nullable = false))})
private EntityAId id;
@Column(name = "STRING_ATTR_1", nullable = false)
private String stringAttribute1;
@Column(name = "STRING_ATTR_2", nullable = false)
private String stringAttribute2;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "entityA")
private Set<EntityB> entityBs;
// getters and setters omitted for brevity.
}
@Entity
@Table(name = "TABLE_2")
public class EntityB {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "partialKey3", column = @Column(name = "PARTIAL_KEY_3", nullable = false)),
@AttributeOverride(name = "partialKey1", column = @Column(name = "PARTIAL_KEY_1", nullable = false)),
@AttributeOverride(name = "partialKey2", column = @Column(name = "PARTIAL_KEY_2", nullable = false))})
private EntityBId id;
@Column(name = "VALUE")
private String value;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "PARTIAL_KEY_1", referencedColumnName = "PARTIAL_KEY_1", nullable = false, insertable = false, updatable = false),
@JoinColumn(name = "PARTIAL_KEY_2", referencedColumnName = "PARTIAL_KEY_2", nullable = false, insertable = false, updatable = false)})
private EntityA entityA;
// getters and setters omitted for brevity.
}
最后是DTO:
public class EntityADto implements Serializable {
private static final long serialVersionUID = -5343329086697620178L;
private String partialKey1;
private Integer partialKey2;
private String stringAttribute1;
private String stringAttribute2;
private Map<String, String> additionalAttributes;
public ProzessdatStandardDto() { }
public ProzessdatStandardDto(String partialKey1,
Integer partialKey2,
String stringAttribute1,
String stringAttribute2,
Set<EntityB> entityBs) {
this.partialKey1 = partialKey1;
this.partialKey2 = partialKey2;
this.stringAttribute1 = stringAttribute1;
this.stringAttribute2 = stringAttribute2;
final Map<String, String> entityBsConverted = new HashMap<>();
if (!CollectionUtils.isEmpty(entityBs)) {
for (EntityB entityB : entityBs) {
entityBsConverted.put(entityB.getPartialKey3(), entityB.getValue());
}
}
this.additionalAttributes = prozessdatExpansionsConverted;
}
// getters and setters omitted for brevity.
}
连接为您提供sql中的行集合:
Parent Child
p1 c1
p1 c2
p1 c3
等等。 没有将结果集合传递给构造函数的机制。
JPA Spec 4.14
constructor_expression ::=
NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable
另外,另一个问题是您的查询可能会返回多个父级或子级。
Parent Child Child2
p1 c111 c121
p1 c121
p1 c131 c122
p2 c211 c211
p2 c221 c212
p2 c231
我猜测的原因是底层JPA提供程序变得太复杂,无法知道在哪里拆分它或者使用哪些值来传递给子构造函数,或者可能是我不熟悉的更微妙的原因。 最重要的是,它要求您提供用于解析此矩阵的代码,如果您要这样做,您也可以在没有JPA的情况下解析结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.