Java 在 Spring JSP 页面中使用集合时 Hibernate LazyInitializationException

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19047835/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:39:55  来源:igfitidea点击:

Hibernate LazyInitializationException while using collection in Spring JSP page

javaspringhibernatejspspring-mvc

提问by user2219247

I have entities like this:

我有这样的实体:

@Entity
@Table(name = "ASSESSMENT")
public class Assessment {

    //All other fields..

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "assessment")
    @OrderBy(value = "order ASC")
    private List<AssessmentPart> assessmentParts = new LinkedList<>();

    public List<AssessmentPart> getAssessmentParts() {
        return assessmentParts;
    }

    //All other getters/setters
}

The other one:

另一个:

@Entity
@Table(name = "ASSESSMENT_PART")
public class AssessmentPart {

    //All other fields

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ASSESSMENT_ID", nullable = false)
    private Assessment assessment;

    public Assessment getAssessment() {
        return assessment;
    }

    public void setAssessment(Assessment assessment) {
        this.assessment = assessment;
    }

    //All other getters/setters
}

Assessment parts are lazy loaded from assessment entity. I also have spring controller method which is Transactional and loads assessment part from the database:

评估部分是从评估实体延迟加载的。我还有 spring 控制器方法,它是事务性的,并从数据库中加载评估部分:

@Transactional
public void doSomething(String partId, Map<String, Object> model) {

    AssessmentPart assessmentPart = //laods a part with entity manager
    Assessment assessment = assessmentPart.getAssessment(); //Getting the assessments

    model.put("assessmentParts", assessment.getAssessmentParts()); //adding all assessments parts into spring model map
}

Once I've added assessment parts into spring model map, they become available in my JSP page and I am using JSTL to loop through them:

一旦我将评估部分添加到 spring 模型映射中,它们就会在我的 JSP 页面中可用,并且我正在使用 JSTL 来循环它们:

<c:forEach var="assessmentPart" items="${assessmentParts}">
     //Not loading any lazy stuff, just getting an ID of assessment part
</c:forEach>

The exception is thrown from this JSP page:

从这个 JSP 页面抛出异常:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: my.package.something.Assessment.assessmentParts, could not initialize proxy - no Session

How is this possible if this collection has already been loaded in a transaction? I am just trying to loop through, hibernate shouldn't be loading anything at this point, because it was already loaded. Why is this weird thing happening?

如果此集合已在事务中加载,这怎么可能?我只是想循环一下,此时 hibernate 不应该加载任何东西,因为它已经加载了。为什么会发生这种奇怪的事情?

采纳答案by M. Deinum

In the view the entitymanager is already closed and as such the elements in your collections fail to retrieve there properties. The code you wrote in the controller doesn't initialize the elements in the collection (it is a LAZY collection) but only the collection is initialized (not the elements inside it).

在视图中,entitymanager 已经关闭,因此集合中的元素无法检索那里的属性。您在控制器中编写的代码不会初始化集合中的元素(它是一个 LAZY 集合),而只会初始化集合(而不是其中的元素)。

Either force the entitymanager to remain open by configuring the OpenEntityManagerInViewFilterin your web configuration.

通过OpenEntityManagerInViewFilter在您的 Web 配置中配置 ,强制实体管理器保持打开状态。

Or change your controller code to include a call to Hibernate.initializeto properly initialize your collection.

或者更改您的控制器代码以包括调用Hibernate.initialize以正确初始化您的集合。

@Transactional
public void doSomething(String partId, Map<String, Object> model) {

    AssessmentPart assessmentPart = //laods a part with entity manager
    Assessment assessment = assessmentPart.getAssessment(); //Getting the assessments
    Hibernate.initialize(assesment.getAssesmentParts()); // Init collection
    model.put("assessmentParts", assessment.getAssessmentParts()); //adding all assessments parts into spring model map
}

Either that or create a custom query which forces eager fetching of the collection.

或者创建一个自定义查询,强制急切地获取集合。

回答by AppX

Loading AssessmentPart from EntityManager will Load Assessment @ManyToOne(fetch = FetchType.EAGER)

从 EntityManager 加载评估部分将加载评估 @ManyToOne(fetch = FetchType.EAGER)

AssessmentPart assessmentPart = //laods a part with entity manager
Assessment assessment = assessmentPart.getAssessment();

Trying to extract AssessmentParts from assessment wont work since it's not @OneToMany(fetch = FetchType.LAZY)and session is already closed

尝试从评估中提取 AssessmentParts 将不起作用,因为它不是@OneToMany(fetch = FetchType.LAZY)并且会话已经关闭

model.put("assessmentParts", assessment.getAssessmentParts());

Get list of AssessmentParts by Assessment from EntityManager in new method should solve the problem.

使用新方法从 EntityManager 中通过 Assessment 获取 AssessmentParts 列表应该可以解决问题。

回答by Debojit Saikia

Change

改变

model.put("assessmentParts", assessment.getAssessmentParts());

to

Hibernate.initialize(assessment.getAssessmentParts());

to initialize your collection.

初始化您的收藏。

The method call assessment.getAssessmentParts()is not initializing your collection. This method call will just return you a collection wrapper and this wrapper you need to pass to the Hibernate.initializemethod for manual initialization.

方法调用assessment.getAssessmentParts()未初始化您的集合。此方法调用只会返回一个集合包装器,您需要将此包装器传递给Hibernate.initialize手动初始化的方法。

EDIT:

编辑:

With JPA, you can fetch the AssessmentPartsalong with the Assessment(wherever needed) using JPQL Fetch Joins, overriding the default behavior:

使用 JPA,您可以使用 JPQL Fetch Joins 获取(在需要AssessmentPartsAssessment地方),覆盖默认行为:

SELECT a FROM Assessment asmt LEFT JOIN FETCH asmt.assessmentParts WHERE asmt.id = :id