spring Hibernate 挂起或抛出延迟初始化没有会话或会话被关闭
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6171549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hibernate hangs or throws lazy initialization no session or session was closed
提问by Ernest
I'm enhancing an old Spring/Hibernate application and im stuck. I have a method that reads a file 3000+ lines long, each line has a record which has to be compared with something on the database and then a register has to be added to the database (a many to many table).
我正在增强一个旧的 Spring/Hibernate 应用程序,但我卡住了。我有一种方法可以读取 3000 多行长的文件,每行都有一条记录,必须与数据库中的某些内容进行比较,然后必须将寄存器添加到数据库中(多对多表)。
Tables and relations are
表和关系是
Branchhave many Product, Productare in many Branches.
Branch有很多 Product,Product在很多 Branches 中。
Productshave many Products, and a Categoryhas many Products
产品有很多产品,一个类别有很多产品
And there are more tables that were there and work ok.
还有更多的桌子在那里并且工作正常。
The newtables/objects that I created are Branch, Product, BranchToProduct.
我创建的新表/对象是Branch, Product, BranchToProduct。
Products have a Set of BranchToProductobjects, which have 3 fields
产品有一组 BranchToProduct对象,其中有 3 个字段
I need to add BranchToProduct objects to the set of Product, with the 3 fields filled from info I get from each line of the file.
我需要将 BranchToProduct 对象添加到 Product 集合中,其中 3 个字段是从我从文件的每一行获得的信息中填充的。
I add a simple line and the application throws:
我添加了一个简单的行,应用程序抛出:
product = productDAO.findByModel(stringModel);
failed to lazily initialize a collection of role: com.bamboo.catW3.domain.Product.products, no session or session was closed
product = productDAO.findByModel(stringModel);
未能延迟初始化角色集合:com.bamboo.catW3.domain.Product.products,没有会话或会话被关闭
If i go to hibernate mapping (hbm file) and set the relation product_to_products lazy=false, the line runs fine alone, but if i try to put it on the file cycle the application hangs always at the 18 th line being processed, doesn′t matter which file i use or the order of the content, console stops working, have to shut java killing the process.
如果我转到休眠映射(hbm 文件)并设置关系 product_to_products lazy=false,该行单独运行良好,但如果我尝试将其放在文件周期中,应用程序总是挂在正在处理的第 18 行,不会'不管我使用哪个文件或内容的顺序,控制台停止工作,必须关闭 java 杀死进程。
Either way, in debug, I get a lot of HQL's for a simple find, 13 lines of HQL until I get my error when lazy=true, and a LOT of lines when i use lazy=false and put it on the cycle.
无论哪种方式,在调试中,我都会得到很多 HQL 来进行简单的查找,13 行 HQL 直到我在 lazy=true 时出现错误为止,并且当我使用 lazy=false 并将其放入循环时会得到很多行。
I think I should try to fix the problem with lazy=true.
我想我应该尝试用lazy=true 来解决这个问题。
This situation make me wonder:
这种情况让我想知道:
1.- When lazy=true. How come I cant run a single line of this command this method, but it works fine on other methods of the class?
1.- 当懒惰=真时。为什么我不能在此方法中运行此命令的一行,但它在该类的其他方法上运行良好?
by the way, this is a class called CatalogFacade that implements methods of other clasess: (CategoryFacade,ContainerFacade,ProductFacade,ProductOptionFacade,ProductStatusFacade,UserFacade,EmailFacade,FileFacade,BranchOfficeFacade)
顺便说一下,这是一个名为 CatalogFacade 的类,它实现了其他类的方法: (CategoryFacade,ContainerFacade,ProductFacade,ProductOptionFacade,ProductStatusFacade,UserFacade,EmailFacade,FileFacade,BranchOfficeFacade)
This is the code for the
productDao.find():
这是
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()));
}
}
The exception is been thrown right in this line, at the last for:
异常是在这一行中抛出的,最后是:
pptp.setProductAttributes(new TreeSet<ProductAttribute>(ptp.getProductAttributes()))
in Intelij's debugger, I can see the object wrongly formed from the query:
在 Intelij 的调试器中,我可以看到从查询中错误地形成的对象:
product.getProducts() = {org.hibernate.collection.PersistentSet@4312}unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.
product.getProducts() = {org.hibernate.collection.PersistentSet@4312}无法评估表达式方法抛出“org.hibernate.LazyInitializationException”异常。
How ever the other attributes are ok. This product doesn′t even have other products in the database.
其他属性如何。该产品在数据库中甚至没有其他产品。
UPDATE
更新
DIGGING DEEPER on the situation, inside the
深入了解情况,在内部
product.find(int)
product.find(int)
In the line before I get the exception, we can see on the debug that the product.products array has an error, instead of the value you can see the lazyInitialitationException. How ever, if I call it from another method, the array is find. So it can′t be inside of it EVEN thoughthe method only receives an Integer.
在我得到异常之前的那一行中,我们可以在调试中看到 product.products 数组有错误,而不是您可以看到的值 lazyInitialitationException。但是,如果我从另一种方法调用它,则数组是 find。因此,即使该方法只接收一个整数,它也不能在其中。
Also, we found that this has happened along all the life cycle of the application, some times the staff replicated a method just like it but changing it setting null to this corrupted arrays. So I'm 100% sure this application is consuming more resources then it should.
此外,我们发现这种情况在应用程序的整个生命周期中都发生过,有时工作人员会复制一个方法,就像它一样,但将其更改为将这个损坏的数组设置为 null。所以我 100% 确信这个应用程序消耗的资源比它应该消耗的资源多。
It has views in Flex, and later views in JSTL where created, and depending who is calling the methods, the exceptions are thrown in different ways for the same methods.
它在 Flex 中有视图,在 JSTL 中有创建的视图,并且根据调用方法的人,对于相同的方法以不同的方式抛出异常。
Adding more info. This is how produt.find is implemented in the AbstractDAOImpl:
添加更多信息。这就是在 AbstractDAOImpl 中实现 produt.find 的方式:
public final Object find(Class clazz, Integer id) throws DataAccessException{
return getHibernateTemplate().get(clazz,id);
}
and this is my Transaction Manager configuration, the annotation method described in the first answer by fillip did not work:
这是我的事务管理器配置,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>
<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>
回答by Fil
You're getting a lazy initialization exception because your session is being closed before you access Product's member variables. When the following line is executed:
您收到延迟初始化异常,因为您的会话在您访问 Product 的成员变量之前已关闭。执行以下行时:
Product product= (Product) super.find(Product.class, id)
Hibernate opens a sessions, retrieves what you're looking for, then closes then session. Any fields that have lazy=true are notretrieved at this time; instead, these fields are populated by proxies. When you try to retrieve the actual value of proxied object, it will attempt to go back to the database using the active session to retrieve the data. If no session can be found, you get the exception that you're seeing. Setting lazy=true has advantages because it prevents the entire object graph from being loaded immediately; nested objects are left alone until you specifically ask for them.
Hibernate 打开会话,检索您要查找的内容,然后关闭会话。此时不会检索任何具有 lazy=true 的字段;相反,这些字段由代理填充。当您尝试检索代理对象的实际值时,它将尝试使用活动会话返回数据库以检索数据。如果找不到会话,您会看到您看到的异常。设置 lazy=true 有好处,因为它可以防止整个对象图被立即加载;嵌套对象会被保留,直到您特别要求它们为止。
There are two common techniques to deal with your problem. The first you've already identified, which is setting lazy=false. This is fine if a product always has product attributes, and you typically use a product and it's attributes together. If you often need just the Product object without its attributes, you're creating unnecessary database load.
有两种常用技术可以解决您的问题。您已经确定的第一个,即设置lazy=false。如果产品始终具有产品属性,并且您通常将产品及其属性一起使用,则这很好。如果您经常只需要 Product 对象而不需要它的属性,那么您正在创建不必要的数据库负载。
The second technique is to mark a method as transactional using Spring annotations.
第二种技术是使用 Spring 注释将方法标记为事务性的。
@Transactional
public Product find(Integer id) throws DataAccessException {
}
A few notes:
一些注意事项:
- You need some additional configuration for transactions to work (the @Transactional annotation isn't enough). See herefor more information.
- Best practices dictate that you annotate methods in your service layer, rather than your data access layer.
- 您需要一些额外的配置才能使事务工作(@Transactional 注释是不够的)。请参阅此处了解更多信息。
- 最佳实践要求您在服务层而不是数据访问层中注释方法。
回答by zclark
I've had the same problem before, and fixed it by using different hibernate methods. I use
我以前遇到过同样的问题,并通过使用不同的休眠方法修复了它。我用
getHibernateTemplate().loadAll(class)
for getting all of something, and
得到所有的东西,和
getHibernateTemplate().get(class, id)
to find a single thing. Both of those I use with no problems. I found that .find() gives me that session was closed error.
找到一个东西。我用的这两个都没有问题。我发现 .find() 给了我会话已关闭错误。
I haven't really looked into why this is though.
我还没有真正研究为什么会这样。
The only other option I can think of aside from using another method is to open and close sessions yourself, but I presume you prefer not to do that.
除了使用另一种方法之外,我能想到的唯一其他选择是自己打开和关闭会话,但我认为您不希望这样做。
回答by bogdan.rusu
Replace the load method with the get method..
用 get 方法替换 load 方法..
I found out after doing more research that the load method doesn't really load the object from the database. Instead it automatically returns a proxy object. Load assumes the object has already been "gotten with get" from the database and is in the cache.
我在做了更多研究后发现 load 方法并没有真正从数据库中加载对象。相反,它会自动返回一个代理对象。Load 假定该对象已经从数据库“通过 get 获取”并且在缓存中。
Just use get instead of load if you want to make sure you hit the database, and make sure you know the difference between these 2 methods.
如果您想确保访问数据库,只需使用 get 而不是 load ,并确保您知道这两种方法之间的区别。
Source: this spring forum comment
来源:今春论坛评论
I've personally test this and it is right, the load method don't retrieve all the wanted data from the DB. Using get fixed my problem.
我亲自测试过,这是正确的,加载方法不会从数据库中检索所有想要的数据。使用 get 解决了我的问题。

