![](/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.