在今天的帖子中,我们将讨论常见的LazyInitializationException错误。 我们将看到四种避免该错误的方法,以及每种方法的优缺点。在本文的最后,我们将讨论EclipseLink如何处理该异常。

为了看到LazyInitializationException错误并进行处理,我们将使用带有EJB 3的应用程序JSF 2。

帖子主题:

  • 了解问题后,为什么会发生LazyInitializationException?
  • 通过注释加载集合
  • 通过View中的Open Session加载收集(View中的事务)
  • 使用PersistenceContextType.EXTENDED的有状态EJB加载收集
  • 通过联接查询加载集合
  • EclipseLink和惰性集合初始化

在本文的结尾,您将找到要下载的源代码。

注意 :在本文中,我们将找到一个简单的代码,该代码不适用设计模式。 本文的重点是展示LazyInitializationException的解决方案。

您将在这里找到的解决方案适用于Web技术,例如带Struts的JSP,带VRaptor的JSP,带Servlet的JSP,带其他功能的JSF。

模型类

在今天的帖子中,我们将使用“人与狗”类:

package com.model;import javax.persistence.*;@Entity
public class Dog {@Id@GeneratedValue(strategy = GenerationType.AUTO)private int id;private String name;public Dog() {}public Dog(String name) {this.name = name;}//get and set
}
package com.model;import java.util.*;import javax.persistence.*;@Entity
public class Person {@Id@GeneratedValue(strategy = GenerationType.AUTO)private int id;private String name;@OneToMany@JoinTable(name = 'person_has_lazy_dogs')private List<Dog> lazyDogs;public Person() {}public Person(String name) {this.name = name;}// get and set
}

注意,通过这两个类,我们将能够创建LazyInitializationException。 我们有一个带狗名单的人类。

我们还将使用一个类来处理数据库操作(EJB DAO),并使用ManagedBean来帮助我们创建错误并进行处理:

package com.ejb;import java.util.List;import javax.ejb.*;
import javax.persistence.*;import com.model.*;@Stateless
public class SystemDAO {@PersistenceContext(unitName = 'LazyPU')private EntityManager entityManager;private void saveDogs(List<Dog> dogs) {for (Dog dog : dogs) {entityManager.persist(dog);}}public void savePerson(Person person) {saveDogs(person.getLazyDogs());saveDogs(person.getEagerDogs());entityManager.persist(person);}// you could use the entityManager.find() method alsopublic Person findByName(String name) {Query query = entityManager.createQuery('select p from Person p where name = :name');query.setParameter('name', name);Person result = null;try {result = (Person) query.getSingleResult();} catch (NoResultException e) {// no result found}return result;}
}
package com.mb;import javax.ejb.EJB;
import javax.faces.bean.*;import com.ejb.SystemDAO;
import com.model.*;@ManagedBean
@RequestScoped
public class DataMB {@EJBprivate SystemDAO systemDAO;private Person person;public Person getPerson() {return systemDAO.findByName('Mark M.');}
}

为什么会发生LazyInitializationException?

Person类具有一个Dog列表。 显示人员数据的最简单,最胖的方法是使用entityManager.find()方法并遍历页面(xhtml)中的集合。

我们想要的只是让代码波纹管做到这一点……

// you could use the entityManager.find() method alsopublic Person findByName(String name) {Query query = entityManager.createQuery('select p from Person p where name = :name');query.setParameter('name', name);Person result = null;try {result = (Person) query.getSingleResult();} catch (NoResultException e) {// no result found}return result;}
public Person getPerson() {return systemDAO.findByName('Mark M.');}
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN''http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
<html xmlns='http://www.w3.org/1999/xhtml'xmlns:f='http://java.sun.com/jsf/core'xmlns:h='http://java.sun.com/jsf/html'xmlns:ui='http://java.sun.com/jsf/facelets'>
<h:head></h:head>
<h:body><h:form><h:dataTable var='dog' value='#{dataMB.personByQuery.lazyDogs}'><h:column><f:facet name='header'>Dog name</f:facet>#{dog.name}</h:column></h:dataTable></h:form>
</h:body>
</html>

注意,在上面的代码中,我们要做的就是在数据库中找到一个人并将其狗显示给用户。 如果您尝试使用上面的代码访问该页面,则会看到以下异常:

[javax.enterprise.resource.webcontainer.jsf.application] (http–127.0.0.1-8080-2) Error Rendering View[/getLazyException.xhtml]: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.model.Person.lazyDogs, no session or session was closed                  at org.hibernate.collection.internal.AbstractPersistentCollection.
throwLazyInitializationException(AbstractPersistentCollection.java:393)
[hibernate-core-4.0.1.Final.jar:4.0.1.Final]at org.hibernate.collection.internal.AbstractPersistentCollection.
throwLazyInitializationExceptionIfNotConnected
(AbstractPersistentCollection.java:385) [
hibernate-core-4.0.1.Final.jar:4.0.1.Final]at org.hibernate.collection.internal.AbstractPersistentCollection.
readSize(AbstractPersistentCollection.java:125) [hibernate-core-4.0.1.Final.jar:4.0.1.Final]

为了更好地理解此错误,让我们看看JPA / Hibernate如何处理这种关系。

每次我们在数据库中进行查询时,JPA都会带入该类的所有信息。 这个规则的例外是当我们谈论列表(集合)时。 我们拥有一个公告对象的图像,其中包含70,000封将接收此公告的电子邮件列表。 如果您只想在屏幕上向用户显示公告名称,请想象一下,如果将70,000封电子邮件加载了该名称,JPA的工作就可以了。

JPA为类属性创建了一种名为“延迟加载”的技术。 我们可以通过以下方式定义延迟加载:“仅在需要时才从数据库加载所需的信息”。

注意,在上面的代码中,数据库查询将返回一个Person对象。 当您访问lazyDogs集合时,容器将注意到lazyDogs集合是一个lazy属性,它将“询问” JPA以从数据库加载该集合。

在执行查询的那一刻( 将带来lazyDogs集合 ),将发生异常。 当JPA / Hibernate尝试访问数据库以获取此惰性信息时,JPA将注意到没有打开的集合。 这就是为什么发生异常(缺少打开的数据库连接)的原因。

默认情况下,每个以@Many结尾的关系都会被延迟加载:@OneToMany和@ManyToMany。 默认情况下,将急切加载以@One结尾的每个关系:@ManyToOne和@OneToOne。 如果要设置延迟加载的基本字段(例如,字符串名称),请执行:@Basic(fetch = FetchType.LAZY)。

如果开发人员未将每个基本字段(例如,String,int,double)放在类中,我们将立即加载它们。

关于默认值的一个有趣主题是,对于同一批注,您可能会发现每个JPA实现(EclipseLink,Hibernate,OpenJPA)具有不同的行为。 我们将在稍后讨论。

通过注释加载集合

加载对象时,最简单,最胖的方法是通过注释添加惰性列表。 但这永远不是最好的方法

在下面的代码中,我们将介绍如何通过注释热切地加载集合:

@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = 'person_has_eager_dogs')
private List<Dog> eagerDogs;
<h:dataTable var='dog' value='#{dataMB.person.eagerDogs}'><h:column><f:facet name='header'>Dog name</f:facet>#{dog.name}</h:column>
</h:dataTable>

这种方法的优点和缺点:

优点

缺点

易于设置

如果该类具有多个集合,则这将不利于服务器性能

该列表将始终与加载的对象一起提供

如果只想显示名称或年龄之类的基本类属性,则将所有配置为EAGER的集合加载名称和年龄

如果EAGER集合只有几个项目,则此方法将是一个很好的选择。 如果此人只有2条,3条狗,则您的系统将能够非常轻松地处理它。 如果稍后“ Persons狗”收集开始确实增长很多,那么这对服务器性能将不会有好处。

这种方法可以应用于JSE和JEE。

通过View中的Open Session加载收集(View中的事务)

在视图中打开会话(或在视图中打开事务)是一种设计模式,您将使数据库连接保持打开状态,直到用户请求结束。 当应用程序访问一个惰性集合时,Hibernate / JPA会进行数据库查询而不会出现问题,不会引发任何异常。

当将此设计模式应用于Web应用程序时,将使用实现Filter的类,该类将接收所有用户请求。 此设计模式非常容易应用,并且有两个基本操作:打开数据库连接和关闭数据库连接。

您将需要编辑“ web.xml ”并添加过滤器配置。 在下面检查我们的代码如何:

<filter><filter-name>ConnectionFilter</filter-name><filter-class>com.filter.ConnectionFilter</filter-class></filter><filter-mapping><filter-name>ConnectionFilter</filter-name><url-pattern>/faces/*</url-pattern></filter-mapping>
package com.filter;import java.io.IOException;import javax.annotation.Resource;
import javax.servlet.*;
import javax.transaction.UserTransaction;public class ConnectionFilter implements Filter {@Overridepublic void destroy() {}@Resourceprivate UserTransaction utx;@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {try {utx.begin();chain.doFilter(request, response);utx.commit();} catch (Exception e) {e.printStackTrace();}}@Overridepublic void init(FilterConfig arg0) throws ServletException {}
}
<h:dataTable var='dog' value='#{dataMB.person.lazyDogs}'><h:column><f:facet name='header'>Dog name</f:facet>#{dog.name}</h:column>
</h:dataTable>

这种方法的优点和缺点:

优点

缺点

模型类别将不需要编辑

所有交易必须在过滤器类中处理

开发人员必须对数据库事务错误非常谨慎。 可以通过ManagedBean / Servlet发送成功消息,但是当数据库提交事务时,可能会发生错误。

可能会发生N + 1效应(如下所示)

这种方法的主要问题是N + 1效应。 当该方法将一个人返回到用户页面时,该页面将迭代dogs集合。 当页面访问惰性集合时,将触发新的数据库查询以显示狗的惰性列表。 想象一下,如果狗有狗的集合,那么狗就是孩子。 为了加载狗子列表,将触发其他数据库查询。 但是,如果孩子有其他孩子,那么JPA再次会触发一个新的数据库查询……然后就可以了……

这是这种方法的主要问题。 一个查询几乎可以创建无限多个其他查询。

这种方法可以应用于JSE和JEE。

继续本教程的第二部分 。

参考: uaiHebert博客上的JCG合作伙伴 Hebert Coelho 对LazyInitializationException的四个解决方案 。

翻译自: https://www.javacodegeeks.com/2012/07/four-solutions-to-lazyinitializationexc_05.html

LazyInitializationException的四种解决方案–第1部分相关推荐

  1. LazyInitializationException的四种解决方案–第2部分

    本文从教程​​的第1部分继续. 使用PersistenceContextType.EXTENDED的有状态EJB加载收集 该方法只能应用于与Full JEE环境兼容的应用程序:将EJB与Persist ...

  2. LazyInitializationException的四个解决方案–第2部分

    本文从本教程的第1部分继续. 有状态EJB使用PersistenceContextType.EXTENDED进行负载收集 该方法只能应用于与Full JEE环境兼容的应用程序:将EJB与Persist ...

  3. iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)

    2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...

  4. Android软键盘遮挡的四种解决方案

    Android软键盘遮挡的四种解决方案 参考文章: (1)Android软键盘遮挡的四种解决方案 (2)https://www.cnblogs.com/jerehedu/p/4194125.html ...

  5. 面试必备:缓存穿透,缓存雪崩的四种解决方案

    前言 设计一个缓存系统,不得不考虑的问题就是:缓存穿透.缓存击穿与失效时的雪崩效应. 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数 ...

  6. beyond compare4过期解决方法_面试必备:缓存穿透、雪崩解决方案及缓存击穿的四种解决方案...

    前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透.缓存击穿与失效时的雪崩效应. 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到 ...

  7. 详解MySQL双活同步复制四种解决方案

    详解MySQL双活同步复制四种解决方案 参考文章: (1)详解MySQL双活同步复制四种解决方案 (2)https://www.cnblogs.com/wuchangsoft/p/10390552.h ...

  8. win10远程计算机证书错误,win10系统下出现Wi-Fi证书错误的四种解决方案

    wifi想必大家都很熟悉吧,这是很多用户们喜欢的无线网络,但是在使用过程中也常常会遇到问题,比如近日就有不少win10系统的用户反馈说在连接wifi的时候,出现了wifi证书错误的情况,导致无法连接到 ...

  9. 计算机突然无法连接网络,win7电脑突然不能上网的四种解决方案

    现如今网络已经非常普及了,基本上家家户户都实现电脑上网了,在深度技术Win7系统上网的时候会遇到电脑突然不能上网的问题,有什么办法能够解决不能上网问题呢?电脑突然不能上网一般都是我们更改了电脑的设置后 ...

最新文章

  1. [分享]iOS开发-UI篇:CAlayer层的属性
  2. FungalTraits: 超越FUNGuild的最新真菌表型数据库
  3. 2、Flutter Widget(IOS Style) - CupertinoActionSheet;
  4. VTK修炼之道2_VTK体系结构1
  5. Kafka监控架构设计
  6. mysql 分类计数器_PHP MySQL映像计数器
  7. 《大数据》第1期“动态”——站在大数据的风口上
  8. C++多线程强制终止
  9. Vue3学习之第四节:setup()中使用watch、watchEffect 函数
  10. gitlab 安装、配置、清空、卸载、重装
  11. asp小偷转html,ASP中实现小偷程序的原理和简单示例
  12. 小脚丫 LCMXO2 4000HC FPGA入门——点个灯
  13. 性能和成本的综合架构:单元化架构
  14. 如何区分固态硬盘和机械硬盘
  15. 机器学习-GB、GBDT、XGboost、Adaboost
  16. 视频教程-SpringBoot核心技术-Java
  17. c语言程序设计 自考,自考“C语言程序设计”模拟试题九
  18. linux系统php安装sockets扩展
  19. TestFlight APP测试(IOS如何让上架前给其他人测试)
  20. java主程序怎样调用子程序_主程序调用子程序使用( )指令。

热门文章

  1. java中的native关键字有什么作用?(java本地方法)
  2. 基于Apache POI 从xlsx读出数据
  3. 一个会定时执行的方法
  4. redis 受攻击怎么办?_最受欢迎的6个最常用的Redis库
  5. java 迁移数据_Java 10迁移建议
  6. openhub_OpenHub框架–下一个有趣的功能
  7. intellij远程调试_IntelliJ中的远程调试Wildfly应用程序
  8. java 存储数据到文件中_本机速度文件支持的“纯” Java大数据存储
  9. spring-bean版本_如何模拟Spring bean(版本2)
  10. 如何使用java代码生成_使用Java成功生成代码的7个技巧