2016-08-16 110 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語句。

請注意,您必須定義與您的多選對應的適當構造函數。