![](/img/trans.png)
[英]How to fix LazyInitializationException in hibernate JPA
[英]How to avoid LazyInitializationException with nested collections in JPA-Hibernate?
強制性背景信息:
作為學習 Spring 的學習的一部分,我構建了我常用的應用程序 - 一個保存問題並隨后使用它們創建隨機測驗的小工具。
每個主題可以有任意數量的主題,這些主題又可以有任意數量的問題,而這些問題又可以有任意數量的答案。
現在,問題正確:
我不斷收到 LazyInitializationExceptions。
我最后嘗試的:
我將幾乎所有用於 Set 的集合類型都更改了。 也很想將 enable_lazy_load_no_trans 屬性設置為 true,但我一直認為這是要避免的反模式。
實體:(僅顯示字段以避免代碼引起的疲勞牆)
主題:
@Entity
@Table(name = Resources.TABLE_SUBJECTS)
public class Subject implements DomainObject
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = Resources.ID_SUBJECT)
private int subjectId;
@Column(name="subject_name", nullable = false)
private String name;
@OneToMany(
mappedBy = Resources.ENTITY_SUBJECT,
fetch = FetchType.EAGER
)
private Set<Topic> topics;
}
話題:
@Entity
@Table(name = Resources.TABLE_TOPICS)
public class Topic implements DomainObject
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "topic_id")
private int topicId;
@Column(name = "name")
private String name;
@OneToMany(
mappedBy = Resources.ENTITY_TOPIC,
orphanRemoval = true,
cascade = CascadeType.MERGE,
fetch = FetchType.EAGER
)
private Set<Question> questions;
@ManyToOne(
fetch = FetchType.LAZY
)
private Subject subject;
}
問題:
@Entity
@Table(name = Resources.TABLE_QUESTIONS)
public class Question implements DomainObject
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = Resources.ID_QUESTION)
private int questionId;
@Column(name = "statement")
private String statement;
@OneToMany(
mappedBy = Resources.ENTITY_QUESTION,
orphanRemoval = true,
cascade = CascadeType.MERGE
)
private Set<Answer> answers;
@ManyToOne(
fetch = FetchType.LAZY
)
private Topic topic;
}
回答:
@Entity
@Table(name = Resources.TABLE_ANSWERS)
public class Answer implements DomainObject
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = Resources.ID_ANSWER)
private int answerId;
@Column(name = "answer_text", nullable = false)
private String text;
@Column(name = "is_correct", nullable = false)
private Boolean isCorrect;
@ManyToOne(
fetch = FetchType.LAZY
)
private Question question;
}
我正在使用擴展 JpaRepository 的接口來執行 CRUD 操作。 我試過這個來取東西,但沒有運氣:
public interface SubjectRepository extends JpaRepository<Subject, Integer>
{
@Query
Optional<Subject> findByName(String name);
@Query(value = "SELECT DISTINCT s FROM Subject s " +
"LEFT JOIN FETCH s.topics AS t " +
"JOIN FETCH t.questions AS q " +
"JOIN FETCH q.answers as a")
List<Subject> getSubjects();
}
現在,一大塊文本 Spring 引導向我投擲 - 堆棧跟蹤:
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [org.callisto.quizmaker.domain.Subject#1] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:322) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.callisto.quizmaker.domain.Subject$HibernateProxy$B8rwBfBD.getTopics(Unknown Source) ~[main/:na]
at org.callisto.quizmaker.service.QuizMakerService.activeSubjectHasTopics(QuizMakerService.java:122) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.checkIfActiveSubjectHasTopics(QuizMaker.java:307) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.createNewQuestion(QuizMaker.java:117) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.prepareMainMenu(QuizMaker.java:88) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.run(QuizMaker.java:65) ~[main/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:769) ~[spring-boot-2.6.3.jar:2.6.3]
當我調用這行代碼時會發生此異常:
boolean output = service.activeSubjectHasTopics();
反過來,它在服務 class 上調用此方法:
public boolean activeSubjectHasTopics()
{
if (activeSubject == null)
{
throw new NullPointerException(Resources.EXCEPTION_SUBJECT_NULL);
}
return !activeSubject.getTopics().isEmpty();
}
在此上下文中調用 activeSubjectHasTopics 方法:
private void createNewQuestion(View view, QuizMakerService service)
{
int subjectId = chooseOrAddSubject(view, service);
service.setActiveSubject(subjectId);
if (checkIfActiveSubjectHasTopics(view, service))
{
chooseOrAddTopic(view, service, subjectId);
}
do
{
createQuestion(view, service);
createAnswers(view, service);
}
while(view.askToCreateAnotherQuestion());
service.saveDataToFile();
prepareMainMenu(view, service);
}
private boolean checkIfActiveSubjectHasTopics(View view, QuizMakerService service)
{
boolean output = service.activeSubjectHasTopics();
if (!output)
{
view.printNoTopicsWarning(service.getActiveSubjectName());
String topicName = readTopicName(view);
createNewTopic(service, topicName);
}
return output;
}
如果您將結構更改為設置,則無濟於事。 如果您需要獲取實體,您需要在 hql 查詢中顯式包含 FETCH 子句。 您將通過查看 Hibernate 文檔來按自己的方式工作: https://docs.jboss.org/hibernate/orm/3.3/performanceing.html/
由於 Christian Beikov 的評論,我能夠找到問題的原因 - 引用:
你從哪里得到這個 activeSubject object? 如果您不將其作為 activeSubjectHasTopics 中事務的一部分加載,那么這將不起作用,因為此時 object 已經分離,因為它是通過不同的事務加載的。
activeSubject
object 被定義為包含activeSubjectHasTopics
方法的服務 class 的一部分,並如他所指出的那樣由不同的事務初始化。
我能夠通過將服務 class 注釋為@Transactional
並存儲我需要的對象的 ID 而不是對象本身來解決問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.