Spring 和 Hibernate:DAO 模式。如何解决 LazyInitializationException

Posted

技术标签:

【中文标题】Spring 和 Hibernate:DAO 模式。如何解决 LazyInitializationException【英文标题】:Spring and Hibernate: DAO Pattern. How to solve LazyInitializationException 【发布时间】:2012-09-28 14:58:45 【问题描述】:

我想知道:FetchType.LAZY 在一个(多)对多中使用 DAO 模式有什么意义? 基本没用?一旦您在 DAO 之外(例如,实际工作是否完成),您就无法获取相关数据,因为您不再处于休眠会话中。

举个例子:

StudentClass。一个学生上很多课。他现在登录系统并从系统中检索他的学生实体对象。

application layer -> Service Layer -> DAO

现在学生想要查看他参加了哪些课程,并且由于我们在 DAO 之外而发生了 LazyInitializationException

有哪些选项可以防止这种情况发生?我喜欢用谷歌搜索时间,但没有找到解决方案,除了在离开 DAO 之前实际获取所有内容,这首先违背了懒惰的目的。 (已阅读有关 OpenSessionViewFilter 的信息,但这应该独立于应用程序层)

您如何以一种好的方式解决这个问题?什么是不受此影响的替代模式?

编辑:

使用以下设置我没有得到LazyInitializationException

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.compound", 
        cascade = CascadeType.ALL, orphanRemoval = true)
@Fetch(FetchMode.JOIN)

有趣的是它一定是这样的。

删除@Fetch -> LazyInitializationException

更奇怪的是,如果我删除了orphanRemoval = true,那么即使使用@FetchLazyInitializationException 也会出现。所以这两个都是必需的。

也许有人可以告诉我为什么会这样。目前,我倾向于完全放弃休眠,就像使用纯 JDBC 一样,我会在几小时前达到预期的行为......

【问题讨论】:

【参考方案1】:

您始终可以在没有相同会话的情况下获取外键关系数据。由于您的会话不存在于应用程序层之外,您只需在检索数据并设置它的方法中手动获取它。

应用层:

public List<SchoolClass> getSchoolClassesByStudent(Serializable identifier)

    List<SchoolClasses> schoolClasses = // get classes by student using criteria or hql
    return schoolClasses;

客户端层:

public void loadSchoolClassesByStudent(Student student)

    student.setSchoolClasses(application.getSchoolClassesByStudent(student.getId()));

我自己选择不支持我的休眠实体中的任何集合。 我使用服务器提供给客户端的非常通用的方法获取所有子关系,类似于这个。

编辑: 或者创建一些逻辑(拦截器?),可以在 DAO 外部检查数据是否在访问之前未初始化,并使用通用方法对其进行初始化。 这也将假设 Hibernate jar 位于客户端级别,这取决于这是否是一个好主意(如果未初始化的数据未设置为 null,则相同)。

【讨论】:

但基本上你正在创建一个使用你的实体类的开发人员必须遵循的约定。我希望自动处理。 我也更喜欢这个。但是,如果您的 DAO 之外的 Session 不存在,(基本上它是 StatelessSession )我没有看到任何其他方法,然后为您的实体编写通用初始化方法,您可以从 DAO(客户端)外部调用。或者将未初始化的数据设置为空,在客户端做空检查。或者创建一些逻辑,可以在 DAO 外部检查数据是否在访问之前未初始化,并使用通用方法对其进行初始化。 好的接受了这个答案。我的解决方案是将渴望从所有者加载到孩子,而不是相反。这不是问题,因为在一般情况下,孩子的数量通常非常少( 【参考方案2】:

解决问题的一种方法是使用OpenSessionInViewFilter 过滤器。

<filter>
    <filter-name>hibernateSessionFilter</filter-name>
    <filter-class>
        org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    </filter-class>
</filter>

【讨论】:

这不只适用于网络应用吗?我的解决方案应该适用于任何表示层。 @beginner_ 你说得对,我认为这是一个 Web 应用程序。 - 我错了吗? 不是用于持久化特殊类型数据的框架。所以表示层可以是任何东西(网络或摇摆或......)。这取决于使用它的开发人员。 @beginner_:在这种情况下,您应该要求框架的用户正确处理 Hibernate Session 和 Transactions。当然,您应该附上一个如何做的例子。 (至少您的框架用户必须像 OpenSessionInViewFilter 一样打开 Hibernate 会话,如果用户随后使用 OpenSessionInViewFilter(如果他有 Web 应用程序)或手动打开它,则完全由用户决定。) 部分想法是用户不需要为休眠而烦恼,而是专注于表示层。

以上是关于Spring 和 Hibernate:DAO 模式。如何解决 LazyInitializationException的主要内容,如果未能解决你的问题,请参考以下文章

DAO、Spring 和 Hibernate

Spring+Hibernate,Autowire sessionFactory 到 hibernate DAO

Spring 与 Hibernate DAO 层?

Spring 3,带有通用 DAO 的 Hibernate 4 AutoWired sessionFactory

在DAO,服务层架构中使用spring MVC和Hibernate的正确方法是啥

通用 DAO hibernate 4 + spring 4 和异常处理