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).
Tables and relations are
Branch have many Product , Product are in many Branches.
Products have many Products, and a Category has many Products
And there are more tables that were there and work ok.
The new tables/objects that I created are Branch, Product, BranchToProduct.
Products have a Set of BranchToProduct objects, which have 3 fields
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.
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
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.
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.
I think I should try to fix the problem with 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?
by the way, this is a class called CatalogFacade that implements methods of other clasess: (CategoryFacade,ContainerFacade,ProductFacade,ProductOptionFacade,ProductStatusFacade,UserFacade,EmailFacade,FileFacade,BranchOfficeFacade)
This is the code for the
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:
product.getProducts() = {org.hibernate.collection.PersistentSet@4312}unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.
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)
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 though the method only receives an Integer.
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.
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.
Adding more info. This is how produt.find is implemented in the AbstractDAOImpl:
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:
<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>
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) 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 not retrieved 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.
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.
The second technique is to mark a method as transactional using Spring annotations.
@Transactional
public Product find(Integer id) throws DataAccessException {
}
A few notes:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With