Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA Criteria multiselect with fetch

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..

like image 856
Gazeciarz Avatar asked Aug 16 '16 13:08

Gazeciarz


People also ask

How can you select multiple fields using a criteria API query?

How can you select multiple fields using a criteria API query? 1: select * from STUDENT; 2: select ID, NAME from STUDENT; 3: select count(ID) from STUDENT; 4: SELECT s.NAME, a. /** create a Criteria Builder **/ CriteriaBuilder builder = em. CriteriaQuery<Long> cq = builder.

What is criteria query multiselect?

CriteriaQuery. multiselect. This method takes one or more Selection items as parameters. The parameters specify the result returned by the Criteria Query. The multiselect method can be used to select a single entity, single or multiple values of the same entity or of different entities.

What is CriteriaBuilder in Java?

public interface CriteriaBuilder. Used to construct criteria queries, compound selections, expressions, predicates, orderings. Note that Predicate is used instead of Expression<Boolean> in this API in order to work around the fact that Java generics are not compatible with varags. Since: Java Persistence 2.0.

What is tuple in hibernate?

The Tuple class is a central data structure in Spring XD. It is an ordered list of values that can be retrieved by name or by index. Tuples are created by a TupleBuilder and are immutable. The values that are stored can be of any type and null values are allowed.


1 Answers

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());
like image 129
maxenglander Avatar answered Sep 21 '22 17:09

maxenglander