Tuesday, May 25, 2010

LazyInitializationException exceptions in JSF beans

LazyInitializationException exceptions in JSF beans

In our web project we were using a mysql DB. Like in most cases our tables were connected between them: user to company, role to user etc. Foreign keys were created, and the beans were connected to (with suitable annotations:@ManyToOne, @ManyToMany()). After defining all this, we expected that when getting an object of company, let’s say, it will contain all its users, and for every user we could get its roles, and for every role its name etc…

But it wasn’t like this. After getting the company object, while trying to get it’s users:

Set <users>=company.getUsers();

The set was null.

What was happening?

Lazy

To improve the performace, our beans were defined with a lazy annotation above the property getter: @OneToMany(fetch = FetchType.LAZY…), that meant when getting to parent object, in our case the object Company, it saved a reference to the child objects: users. While the session is still open, Hibernate knows where to go and get the data. The story was that after the session was closed, the reference still exited but it did not know to where to indicate.

The solution was to define transactions. Control on opening and closing the sessions, and get all the needed data before the transaction closes, before all the references lose their target!

Transactions

Spring have special annotations, for defining such things. The definition is written above every function: @Transactional. This annotation has a few properties, among them: a boolean property readOnly: true- if this transaction is only for reading data from the DB, or false that means that this transaction is coming to insert or update data too. The main property is propagation. It defines what session to use for this method. Its value is the Propagation enum. Its main values that we used are REQUIRES_NEW, REQUIRED.

REQUIRED means that if there is an open transaction it will join it. If not, it will open a new one.

REQUIRES_NEW means that in any case it will open a new transaction.

The annotation should be written above the method:

@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)

This Spring annotations can be recognized only in a class that the spring knows, that were defined in the applicationContext.xml . The definition could be like this:

<bean id="viewDAO" class="com.XXX.infra.daoimpl.GeneralDAOImpl>

</bean>

Using services

As we said, transaction will have an affect only in the spring class. And a class could not be a spring class and a JSF class too. Special classes need to be defined for the hibernate issues.

An interface should be defined with the needed functions. The class defined above should implement it.

In the JSF class you should define an instance of that interface.

private ManageUserService manageUserService= FacesContextUtils.getWebApplicationContext(

FacesContext.getCurrentInstance() ).getBean( name );

The name that’s sent as an argument is the id that defined the implementation in the applicationContext.

The next step is to call the implementation methods from the JSF classes. When first rendering the page, all the data needed from the DB should be taken out all in one transaction. You need to manage the transactions in the Spring class. The main method, the one you call from the your been, should bepropagation = Propagation.REQUIRES_NEW. Any child methods, if there are, should use the same transaction. Its annotation should look propagation = Propagation.REQUIRED, this makes sure that the data will be taken properly before the reference loses its target.

As well, when submitting the page, or every new connection to the DB, should be defined with a new transaction, and that method should end only after you finished doing all what you needed.

If a lot of data needed from that transaction, and methods, we know, return one item only, you could define a class that has the needed properties, and fill it with data in the service.

No comments:

Post a Comment