我正在增强一个旧的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>
答案 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 :(得分:0)
之前我遇到过同样的问题,并使用不同的hibernate方法修复它。我用
getHibernateTemplate().loadAll(class)
获取所有内容,
getHibernateTemplate().get(class, id)
找到一件事。我使用的两个都没有问题。我发现.find()告诉我会话被关闭错误。
我还没有真正研究过为什么会这样。
除了使用其他方法之外,我能想到的另一个选择是自己打开和关闭会话,但我认为你不想这样做。
答案 2 :(得分:0)
用get方法替换load方法..
我在做了更多的研究后发现,load方法并没有真正从数据库中加载对象。相反,它会自动返回代理对象。 Load假定对象已经从数据库中“获取”并且位于缓存中。
如果你想确保你访问数据库,只需使用get而不是load,并确保你知道这两种方法之间的区别。
我亲自测试过这个,并且它是正确的,加载方法不会从数据库中检索所有想要的数据。使用修复我的问题。