Hibernate挂起或抛出延迟初始化没有会话或会话关闭

时间:2011-05-30 01:27:02

标签: hibernate spring

我正在增强一个旧的Spring / Hibernate应用程序并且卡住了。我有一个读取3000多行文件的方法,每行都有一条记录,必须与数据库中的内容进行比较,然后必须将一个寄存器添加到数据库(多对多表)。

表和关系是

分支 多个产品产品位于多个分支中。

产品 许多产品类别 许多产品

还有更多的桌子可以使用。

我创建的表/对象是 Branch,Product,BranchToProduct

产品有一组BranchToProduct 对象,有3个字段

我需要将BranchToProduct对象添加到Product集合中,从我从文件的每一行获取的信息填充3个字段。

我添加一个简单的行,应用程序抛出:

  

产品=   productDAO.findByModel(stringModel);

     

懒得初始化a   角色集合:   com.bamboo.catW3.domain.Product.products,   没有会话或会话被关闭

如果我转到hibernate映射(hbm文件)并将关系设置为product_to_products lazy = false,则该行单独运行正常,但如果我尝试将其放在文件循环中,则应用程序始终挂起正在处理的第18行,无论我使用哪个文件或内容的顺序,控制台停止工作,都必须关闭java查杀过程。

无论哪种方式,在调试中,我得到了很多HQL的简单查找,13行HQL,直到我在lazy = true时得到我的错误,并且当我使用lazy = false并且把它放在周期。

我想我应该尝试用lazy = true解决问题。

这种情况让我想知道:

1.-当lazy = true时。为什么我不能运行此命令的这一行的单行,但它在该类的其他方法上工作正常?

顺便说一下,这是一个名为CatalogFacade的类,它实现了其他clasess的方法:(CategoryFacade,ContainerFacade,ProductFacade,ProductOptionFacade,ProductStatusFacade,UserFacade,EmailFacade,FileFacade,BranchOfficeFacade)

这是中国的代码 productDao.find():

public Product find(Integer id) throws DataAccessException {

        Product product= (Product) super.find(Product.class, id);


        if(product!=null){
            product.setProductAttributes(new TreeSet<ProductAttribute>(product.getProductAttributes()));

            for (Product ptp : product.getProducts()){
               ptp.setProductAttributes(new TreeSet<ProductAttribute>(ptp.getProductAttributes()));

             }

         } 

在这一行中,异常被抛出,最后是:

pptp.setProductAttributes(new TreeSet<ProductAttribute>(ptp.getProductAttributes()))  
在Intelij的调试器中,我可以看到查询中错误形成的对象:

product.getProducts()= {org.hibernate.collection.PersistentSet@4312}无法评估表达式方法抛出'org.hibernate.LazyInitializationException'异常。

其他属性如何还可以。该产品甚至没有数据库中的其他产品。

更新

深入了解情况,

  

product.find(int)的

在我得到异常之前的行中,我们可以在调试中看到product.products数组有错误,而不是你可以看到lazyInitialitationException的值。 如何,如果我从另一个方法调用它,则会找到该数组。所以它不能在它内部即使该方法只接收一个整数。

此外,我们发现这已经发生在应用程序的所有生命周期中,有时候工作人员复制了一个类似于它的方法,但更改它将null设置为此损坏的数组。所以我100%确定这个应用程序正在消耗更多的资源。

它在Flex中有视图,后来在JSTL中创建了视图,并且根据调用方法的人来说,对于相同的方法,异常会以不同的方式抛出。

添加更多信息。这就是在AbstractDAOImpl中实现produt.find的方法:

public final Object find(Class clazz, Integer id) throws DataAccessException{

        return getHibernateTemplate().get(clazz,id);
}

这是我的事务管理器配置,由fillip在第一个答案中描述的注释方法不起作用:

<bean id="catalogFacade" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager"/>
    </property>
    <property name="target">
        <ref local="catalogFacadeTarget"/>
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="add*">PROPAGATION_REQUIRED</prop>
            <prop key="save*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
            <prop key="delete*">PROPAGATION_REQUIRED</prop>
            <prop key="remove*">PROPAGATION_REQUIRED</prop>
            <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
            <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
            <prop key="contains*">PROPAGATION_SUPPORTS,readOnly</prop>
            <prop key="login*">PROPAGATION_SUPPORTS,readOnly</prop>
        </props>
    </property>
</bean>

3 个答案:

答案 0 :(得分:24)

您将获得延迟初始化异常,因为在访问Product的成员变量之前会话正在关闭。执行以下行时:

Product product= (Product) super.find(Product.class, id)

Hibernate会打开会话,检索您要查找的内容,然后关闭会话。任何具有lazy = true的字段此时都会;相反,这些字段由代理填充。当您尝试检索代理对象的实际值时,它将尝试使用活动会话返回到数据库以检索数据。如果找不到会话,您将获得您所看到的例外情况。设置lazy = true具有优势,因为它可以防止立即加载整个对象图;嵌套对象保持不变,直到您特别要求它们为止。

有两种常用技巧可以解决您的问题。你已经确定的第一个,即设置lazy = false。如果产品始终具有产品属性,并且您通常使用产品及其属性,则这很好。如果您经常只需要没有其属性的Product对象,那么您将创建不必要的数据库负载。

第二种技术是使用Spring注释将方法标记为事务性。

@Transactional
public Product find(Integer id) throws DataAccessException {

}

一些注意事项:

  1. 你还需要一些额外的东西 交易的配置 工作(@Transactional注释 还不够。请参阅here 更多信息。
  2. 最佳做法要求您在服务层中注释方法,而不是数据访问层。

答案 1 :(得分:0)

之前我遇到过同样的问题,并使用不同的hibernate方法修复它。我用

getHibernateTemplate().loadAll(class)

获取所有内容,

getHibernateTemplate().get(class, id) 

找到一件事。我使用的两个都没有问题。我发现.find()告诉我会话被关闭错误。

我还没有真正研究过为什么会这样。

除了使用其他方法之外,我能想到的另一个选择是自己打开和关闭会话,但我认为你不想这样做。

答案 2 :(得分:0)

用get方法替换load方法..

我在做了更多的研究后发现,load方法并没有真正从数据库中加载对象。相反,它会自动返回代理对象。 Load假定对象已经从数据库中“获取”并且位于缓存中。

如果你想确保你访问数据库,只需使用get而不是load,并确保你知道这两种方法之间的区别。

来源:this spring forum comment

我亲自测试过这个,并且它是正确的,加载方法不会从数据库中检索所有想要的数据。使用修复我的问题。