2016-08-16 92 views
4

我有以下型号:JPA标准多选与取

@Entity 
@Table(name = "SAMPLE_TABLE") 
@Audited 
public class SampleModel implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "ID") 
    private Long id; 

    @Column(name = "NAME", nullable = false) 
    @NotEmpty 
    private String name; 

    @Column(name = "SHORT_NAME", nullable = true) 
    private String shortName; 

    @ManyToOne(fetch = FetchType.LAZY, optional = true) 
    @JoinColumn(name = "MENTOR_ID") 
    private User mentor; 

//other fields here 

//omitted getters/setters 

} 

现在我想查询仅列:idnameshortNamementor这referes到User实体(不完整的实体,因为它有很多其他的属性,我想有最好的表现)。

当我写查询:

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery<SampleModel> query = builder.createQuery(SampleModel.class); 
Root<SampleModel> root = query.from(SampleModel.class); 
query.select(root).distinct(true); 
root.fetch(SampleModel_.mentor, JoinType.LEFT); 

query.multiselect(root.get(SampleModel_.id), root.get(SampleModel_.name), root.get(SampleModel_.shortName), root.get(SampleModel_.mentor)); 
query.orderBy(builder.asc(root.get(SampleModel_.name))); 
TypedQuery<SampleModel> allQuery = em.createQuery(query); 
return allQuery.getResultList(); 

我有例外如下:

Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.sample.SampleModel.model.SampleModel.mentor,tableName=USER_,tableAlias=user1_,origin=SampleModel SampleModel0_,columns={SampleModel0_.MENTOR_ID ,className=com.sample.credential.model.User}}] 
    at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:214) 
    at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:991) 
    at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:759) 
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:675) 
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:311) 
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:259) 
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262) 
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190) 
    ... 138 more 

查询异常之前:

SELECT DISTINCT NEW com.sample.SampleModel.model.SampleModel(generatedAlias0.id, generatedAlias0.name, generatedAlias0.shortName, generatedAlias0.mentor) 
FROM com.sample.SampleModel.model.SampleModel AS generatedAlias0 
LEFT JOIN FETCH generatedAlias0.mentor AS generatedAlias1 
ORDER BY generatedAlias0.name ASC 

我知道我可以替换连接抓取但随后我会遇到N + 1问题。此外,我没有回从用户到SampleModel中的参考,我不希望有..

回答

1

我遇到了同样的问题,发现我能够用各种各样的变通方法:

CriteriaQuery<Tuple> crit = builder.createTupleQuery(); 

而不是

CriteriaQuery<X> crit = builder.createQuery(X.class); 

需要做一些额外的工作来产生最终结果,例如你的情况:

return allQuery.getResultList().stream() 
    map(tuple -> { 
     return new SampleModel(tuple.get(0, ...), ...)); 
    }) 
    .collect(toList()); 
0

我使用的EclipseLink作为JPA提供了同样的问题:我只是想返回一个映射实体(在Gazeciarz的例子«用户»)的ID。

这可以通过更换(在query.multiselect条款)

root.get(SampleModel_.mentor) 

的东西,如

root.get(SampleModel_.mentor).get(User_.id) 

然后,而不是返回用户的所有领域,要求很简单地实现只会返回它的ID。

我也使用了元组查询,但在我的情况下,这是因为我的查询从多个实体返回文件。

0

问自问已经很长时间了。但我希望其他一些人可以从我的解决方案中受益:

诀窍是使用子查询。

让我们假设你在你的应用程序的实体有申请人(一到一):

@Entity 
public class Application {  

    private long id; 
    private Date date; 

    @OneToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "some_id") 
    private Applicant applicant; 

    // Other fields 

    public Application() {} 

    public Application(long id, Date date, Applicant applicant) { 
     // Setters 
    } 
} 

//............... 

CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 
CriteriaQuery<Application> cbQuery = cb.createQuery(Application.class); 

Root<Application> root = cbQuery.from(Application.class); 

Subquery<Applicant> subquery = cbQuery.subquery(Applicant.class); 
Root subRoot = subquery.from(Applicant.class); 

subquery.select(subRoot).where(cb.equal(root.get("applicant"), subRoot)); 
cbQuery.multiselect(root.get("id"), root.get("date"), subquery.getSelection()); 

此代码将生成应用select语句,并为每个应用程序的每个申请人的SELECT语句。

请注意,您必须定义与您的多选对应的适当构造函数。