生成条件API查询以避免MultipleBagFetchException



我们有一个Lesson实体,每个Lesson都有参加课程的学生和客人的列表:

public class Lesson {
@Id
private Long id;
// ...other properties
@OneToMany(mappedBy = "lesson", cascade = CascadeType.ALL)
private List<Student> students;
@OneToMany(mappedBy = "lesson", , cascade = CascadeType.ALL)
private List<Guest> guests;
// ...constructors, getters and setters
}
// --------------------------------------------------------------------------------------------
public class Student {
@Id
private Long id;
// ...
@ManyToOne
@JoinColumn(name = "lesson_id")
private Lesson lesson;
// ...
}
// -------------------------------------------------------------------------------------------
public class Guest {
@Id
private Long id;
// ...
@ManyToOne
@JoinColumn(name = "lesson_id")
private Lesson lesson;
// ...
}

我想让所有的课程与学生和客人提取,所以我建立了以下查询的标准API:

@Repository
public class LessonCriteriaRepositoryImpl implements LessonCriteriaRepository {
@PersistenceContext
private EntityManager em;
@Override
public List<Lesson> findAll() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Lesson> criteriaQuery = builder.createQuery(Lesson.class);
Root<Lesson> lesson = criteriaQuery.from(Lesson.class);
lesson.fetch(Lesson_.students, JoinType.LEFT);
lesson.fetch(Lesson_.guests, JoinType.LEFT);
criteriaQuery.select(lesson).distinct(true);
TypedQuery<Lesson> query = em.createQuery(criteriaQuery);
return query.getResultList();
}
}

并且我得到MultipleBagFetchException,因为我不能同时获取多个集合。根据Vlad Mihalcea(https://twitter.com/vlad_mihalcea)post(Hibernate抛出MultipleBagFetchException-不能同时获取多个包)击败MultipleBagFetchException的正确方法是进行两个单独的查询并一个接一个地获取集合。

但我无法理解如何使用标准API构建这样两个查询,一个接一个地获取集合(我需要使用标准API,因为我在这里举了一些非常简单的代码作为例子,在真实的应用程序中,我有复杂的过滤器,我使用许多谓词来构建查询)。

基于本文的建议https://vladmihalcea.com/hibernate-multiplebagfetchexception/,我们可以构建两个这样的查询:

@Repository
public class LessonCriteriaRepositoryImpl implements LessonCriteriaRepository {
@PersistenceContext
private EntityManager entityManager;
public List<Lesson> findAll() {
//build first query for fetching students
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Lesson> criteriaQuery = builder.createQuery(Lesson.class);
Root<Lesson> lesson = criteriaQuery.from(Lesson.class);
lesson.fetch("students", JoinType.LEFT);
criteriaQuery.select(lesson).distinct(true);
TypedQuery<Lesson> query1 = entityManager.createQuery(criteriaQuery);
List<Lesson> lessons = query1.getResultList();
//build second query for fetching guests
builder = entityManager.getCriteriaBuilder();
criteriaQuery = builder.createQuery(Lesson.class);
lesson = criteriaQuery.from(Lesson.class);
lesson.fetch("guests", JoinType.LEFT);
criteriaQuery.select(lesson).distinct(true).where(lesson.in(lessons));
TypedQuery<Lesson> query2 = entityManager.createQuery(criteriaQuery);
return query2.getResultList();
}
}

我认为您不需要在查询中获取学生和访客
JPA将负责填写课程中的列表
所以这就足够了:

public List<Lesson> findAll() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Lesson> criteriaQuery = builder.createQuery(Lesson.class);
Root<Lesson> lesson = criteriaQuery.from(Lesson.class);
criteriaQuery.select(lesson);
TypedQuery<Lesson> query = em.createQuery(criteriaQuery);
return query.getResultList();
}

@OneToMany注释中添加fetch = FetchType.EAGER

public class Lesson {
@Id
private Long id;
// ...other properties
@OneToMany(mappedBy = "lesson",fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Student> students;
@OneToMany(mappedBy = "lesson",fetch = FetchType.EAGER , cascade = CascadeType.ALL)
private List<Guest> guests;
// ...constructors, getters and setters
}

然后

public List<Lesson> findAll() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Lesson> criteriaQuery = builder.createQuery(Lesson.class);
Root<Lesson> lesson = criteriaQuery.from(Lesson.class);
criteriaQuery.select(lesson);
TypedQuery<Lesson> query = em.createQuery(criteriaQuery);
return query.getResultList();
}

只需对studentsguests使用Set而不是List

相关内容

  • 没有找到相关文章

最新更新