lazyinitializationexception与简单域模型(SpringBoot/jpa)

dgenwo3n  于 2021-07-06  发布在  Java
关注(0)|答案(0)|浏览(192)

我正在用一个简单的域模型构建一个应用程序。该项目使用springboot和jpa。部分模型如下:
abstractentity.java文件:

@MappedSuperclass
public abstract class AbstractEntity {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Long id;
  ...
}

任务.java:

@Entity
public class Task extends AbstractEntity {
  ...
  @OneToMany(mappedBy = "task")
  private List<TaskNote>           notes        = new ArrayList<>();

  @OneToMany(mappedBy = "task")
  private List<StateChangeHistory> stateHistory = new ArrayList<>();
  ...
}

tasknote.java文件:

@Entity
public class TaskNote extends AbstractEntity {
  ...
  @ManyToOne
  private Task   task;
  ...
}

statechangehistory.java:

@Entity
public class StateChangeHistory extends AbstractEntity {
  @ManyToOne
  private Task           task;
  ...
}

mainview.java(我正在使用vaadin):

@Route("")
@CssImport("./styles/shared-styles.css")
public class MainView extends VerticalLayout {
  private Grid<Task>  grid = new Grid<>(Task.class);
  private TaskService taskService;

  public MainView(TaskService taskService) {
    this.taskService = taskService;
    ...
    updateGrid();
  }
  ...
  private void updateGrid() {
    grid.setItems(taskService.findAll());
  }
}

我正在构建主视图,它当前只有一个 Task s。视图使用 @Service 通过 JpaRepository 实施。
taskrepository.java文件:

public interface TaskRepository extends JpaRepository<Task, Long> {
}

其他类字段也有类似的存储库类(例如。 TaskNote ).
taskservice.java文件:

@Service
public class TaskService {
  private TaskRepository      taskRepository;

  public TaskService(TaskRepository taskRepository) {
    this.taskRepository = taskRepository;
  }
  ...
  // Load some test data.
  @PostConstruct
  public void populateTestData() {
    if (taskRepository.count() == 0) {
      Task task = null;
      TaskNote note = null;

      task = new Task("Task-1");
      note = new TaskNote();
      note.setNote("note");
      task.getNotes().add(note);

      taskRepository.save(task);
      taskRepository.save(new Task("TEST-2"));
    }
  }
}

我遇到了一个 LazyInitializationException ,这显然是一个相当普遍的现象-至少对我们jpa/HibernateNoobs来说。
例外情况如下:

[DEBUG] 2020-11-20 14:50:52.801 [http-nio-8080-exec-2] DispatcherServlet - Failed to complete request: javax.servlet.ServletException: com.vaadin.flow.server.ServiceException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mycompany.tasks.backend.entity.Task.notes, could not initialize proxy - no Session
[ERROR] 2020-11-20 14:50:52.803 [http-nio-8080-exec-2] [dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [com.vaadin.flow.server.ServiceException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mycompany.tasks.backend.entity.Task.notes, could not initialize proxy - no Session] with root cause
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mycompany.tasks.backend.entity.Task.notes, could not initialize proxy - no Session

我开始在google上搜索,发现了一篇有趣的文章,讨论了处理这个问题的正确方法,以及不应该使用的“解决方案”。
以下是链接:https://thorben-janssen.com/lazyinitializationexception/#how_to_fix_the_lazyinitializationexception
我真的不明白这篇文章是怎么写的。我对java中的数据持久性知之甚少。我一直认为spring等的使用使这项工作比过去简单得多。
我从我所看到和读到的东西中得到的要点是,当我得到“父”时,我需要得到下属(例如任务的注解)数据。我想这是有道理的,但我不知道该怎么办。本文中提供的示例似乎需要编写需要相当好的hibernate或jpa知识的代码。这似乎也违背了spring等旨在提供的“简单性”原则。也许没那么复杂,但我知道的还不足以做出判断。
我正在寻找理解和方法来找到一个解决办法,使这项工作。
更新1-11/24/2020
我决定试着用 @NamedEntityGraph 来看看这是否有帮助。然而,我怀疑要想让它真正起作用,我需要用一个 EntityManager 示例。我不确定我应该在哪里使用实体管理器-在 @Service 或者在 TaskRepository . 我还需要做更多的研究来了解如何使用它。这方面的任何建议都会有帮助。
这只是我的印象,但这似乎增加了代码的复杂性,以建立专门的查询逻辑,以便获得 Task 以及所有关联的子对象。这些都是为了避免 LazyInitializationException . spring和/或jpa/hibernate在没有额外定制的情况下不能处理这个问题吗?
我错过了什么?
更新2-11/24/2020
我修改代码如下:
任务.java:

@NamedEntityGraph(name = "graph.task", attributeNodes = { @NamedAttributeNode("notes"),
    @NamedAttributeNode("stateHistory") })
public class Task extends AbstractEntity {
...
}

taskrepository.java文件:

public interface TaskRepository extends JpaRepository<Task, Long>, TaskRepositoryCustom {
}

taskrepositorycustom.java文件:

public interface TaskRepositoryCustom {
  List<Task> findAll();
}

taskrepositorycustomimpl.java文件:

@Repository
@Transactional
public class TaskRepositoryCustomImpl implements TaskRepositoryCustom {

  @PersistenceContext
  private EntityManager entityManager;

  @Override
  public List<Task> findAll() {
    EntityGraph<?> graph = entityManager.getEntityGraph("graph.task");

    TypedQuery<Task> query = entityManager.createQuery("select t from Task t", Task.class)
        .setHint("javax.persistence.fetchgraph", graph);

    return query.getResultList();
  }

}

这似乎解决了问题 LazyInitializationException 但产生了一个新问题:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mycompany.tasks.ui.MainView': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.mycompany.tasks.ui.MainView]: Constructor threw exception; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.mycompany.tasks.backend.entity.Task.stateHistory, com.mycompany.tasks.backend.entity.Task.notes]; nested exception is java.lang.IllegalArgumentException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.mycompany.tasks.backend.entity.Task.stateHistory, com.mycompany.tasks.backend.entity.Task.notes]

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题