Java 使用JPA和Hibernate时如何解决LazyInitializationException

Java 使用JPA和Hibernate时如何解决LazyInitializationException,java,hibernate,jpa,orm,lazy-initialization,Java,Hibernate,Jpa,Orm,Lazy Initialization,我正在为一位希望使用延迟初始化的客户进行一个项目。 当使用默认的延迟加载模式映射类时,它们总是得到“延迟初始化异常” @JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name = "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita

我正在为一位希望使用延迟初始化的客户进行一个项目。 当使用默认的延迟加载模式映射类时,它们总是得到“延迟初始化异常”

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name =    "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")})
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection;
@JoinTable(name=“join_profilo_funzionalita”,joinColumns={@JoinColumn(name=“profilo_id”,referencedColumnName=“profilo_id”)},inverseJoinColumns={@JoinColumn(name=“funzionalita_id”,referencedColumnName=“funzionalita_id”)})
//@ManyToMany(fetch=FetchType.EAGER)-如果未注释,则没有异常
@许多
私人收藏;趣味收藏;
是否有使用JPA类来避免此错误的标准模式


欢迎使用片段,非常感谢您的时间

OpenSessionInView是处理此问题的一种模式。这里有一些信息:

在实现此模式时,您需要谨慎,并理解其含义。每次在视图中导航惰性关联时,它都会触发另一个SQL查询来加载数据。如果您的用例中这些SQL查询的数量和大小都很小,那么这可能无关紧要。至少要确保您调整了日志记录设置,这样您就可以看到Hibernate在后台“神奇地”执行什么样的查询来加载数据


也要考虑你所写的应用程序。如果您没有处理远程处理(没有web服务,没有基于AJAX的web客户机),那么OSIV可能工作得非常好。但是,如果远程处理序列化程序开始遍历整个对象图,它可能会触发大量SQL查询,并使您的DB和app server瘫痪。

LazyInitializationException意味着您在hibernate会话关闭后或在对象从会话分离后调用集合


您需要将对象重新连接到hibernate会话,更改调用集合的位置,或者将会话关闭的边界移到更高的层。

有许多方法可以预取属性,因此在会话关闭后它们就在那里:

  • 调用合适的getter。在将字段提取到bean中之后,它在会话关闭后就在那个里了
  • 您可以在EJBQL查询中初始化字段,查找
    joinfetch
    关键字
  • 启用AvailableSettings.Enable_LAZY_LOAD_NO_TRANS(如果您使用的是支持它的Hibernate版本)
  • 尝试这些解决方案时,可能会出现以下几个问题:

  • getter的调用可能会被JIT编译器优化掉(有时这需要一段时间)
  • 您试图
    加入获取的实体可以通过涉及列表的多个“多”关系进行链接。在这种情况下,结果查询返回不明确的结果,Hibernate将拒绝在单个查询中获取数据
  • 已经有一个了。还有更多,因为正如hibernate的人所说:注意:这可能发生在事务之外,不安全。小心使用。大部分时间你都是一个人
  • 最好的方法是先尝试
    加入FETCH
    。如果这不起作用,尝试getter方法。如果JIT编译器在运行时把结果搞砸了,请将结果分配给
    公共静态易失性对象


    或者停止使用Hibernate…

    当您正在使用集合并且希望使用延迟加载对其进行初始化时,请在会话关闭之前使用该集合。如果会话在此之后关闭,如果您想使用,则会得到lazyinitializeException,因为默认情况下是try。Hibernate 4.1.6最终解决了这个问题:

    您需要设置hibernate属性hibernate.enable\u lazy\u load\u no\u trans=true

    以下是在春天如何做到这一点:

    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="packagesToScan" value="com.mycompany.somepackage"/>
        <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/>
        <property name="jpaDialect" ref="jpaDialect"/>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
            </props>
        </property>
    </bean>
    
    
    真的
    

    瞧;现在,在hibernate会话(JPA speak中的持久化上下文)之外导航域模型时,您不必担心LazyInitializationException。

    注意,您不应该使用
    hibernate。启用\u lazy\u load\u no\u trans
    预hibernate 4.1.7,因为它会泄漏连接。参见

    Oracle Java教程指出,“企业Bean支持事务,即管理共享对象并发访问的机制。”因此,为了处理延迟获取问题,我创建了一个无状态Java会话Bean,然后在从该方法返回之前获取所需的所有子类。这避免了延迟获取异常。Oracle还将其称为“会话外观”核心J2EE模式。此模式似乎比前面提到的其他一些实践更好。

    是在实体查询中使用JOIN-FETCH指令

    这对性能不利。此外,还有反模式,例如:

    您永远不应该使用它,因为它们要求为UI呈现打开数据库连接(在视图中打开会话),或者在初始持久性上下文之外获取的每个惰性关联都需要数据库连接(
    hibernate.enable\u lazy\u load\u no\u trans

    有时,您甚至不需要实体,DTO投影甚至更好。
    仅当需要修改实体时,才应获取实体。对于只读事务,.

    我正在从事一个项目,该项目旨在解决使用ModelMapper将实体映射到DTO时常见的JPA问题。这个问题已经在项目中得到解决。项目链接:

    “将实体声明为延迟加载对性能至关重要,因此 不需要每次需要一些数据时都获取所有相关实体。 但是这种技术会导致一些问题。最常见的是 LazyInitializationException有时会很烦人。 大多数情况下,对于未加载的对象,我们只需要一个null对象 实体,而不是在访问时引发异常的对象…”

    资料来源:

    因此,在
    TypedQuery<SystemEntity> query =
            em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);
    
    SystemEntity system = query.getSingleResult();
    return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);
    
    TypedQuery<SystemEntity> query =
            em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);
    
    SystemEntity system = query.getSingleResult();
    return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);