Multiselect selection JPA

I have the 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 ask only those columns: id, name, shortNameand that refers to the object (not the entire object, since it has many other features, and I would like to have the best performance). mentorUser

When I write a request:

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

Request 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 don't have a backlink from User to SampleModel, and I don't want to have one.

+4
source share
3 answers

I ran into the same problem and found that I was able to get around it using:

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());
+1

, EclipseLink JPA: ( "" Gazeciarz).

, ( query.multiselect)

root.get(SampleModel_.mentor)

-

root.get(SampleModel_.mentor).get(User_.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 .

Note that you need to determine the appropriate constructor that matches your multi-select.

0
source

Source: https://habr.com/ru/post/1651413/


All Articles