I have following model:
@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 }
Now I would like to query only columns: id
, name
, shortName
and mentor
which referes to User
entity (not complete entity, because it has many other properties and I would like to have best performance).
When I write query:
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();
I have following exception:
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
Query before exception:
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
I know that I can replace fetch with join but then I will have N+1 problem. Also I do not have back reference from User to SampleModel and I do not want to have..
Advertisement
Answer
I ran into this same issue, and found that I was able to work around it by using:
CriteriaQuery<Tuple> crit = builder.createTupleQuery();
instead of
CriteriaQuery<X> crit = builder.createQuery(X.class);
A little extra work has to be done to produce the end result, e.g. in your case:
return allQuery.getResultList().stream() map(tuple -> { return new SampleModel(tuple.get(0, ...), ...)); }) .collect(toList());