Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate hangs or throws lazy initialization no session or session was closed

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>
like image 945
Ernest Avatar asked May 30 '11 01:05

Ernest


1 Answers

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:

  1. You need some additional configuration for transactions to work (the @Transactional annotation isn't enough). See here for more information.
  2. Best practices dictate that you annotate methods in your service layer, rather than your data access layer.
like image 192
Fil Avatar answered Oct 08 '22 03:10

Fil