views:

344

answers:

4

Hello,

I am not a beginner in hibernate, but I am a beginner in working with Hibernate in Spring. I read the book Spring in Action. So I wrote a small application to comprehend the examples in the book. Here an extraction of my application. I can give you more, if you want.

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public void runQuery()
{
    final BuildingDAO buildingDAO = ( BuildingDAO ) applContext.getBean( "buildingDAO" );
    final Building building = buildingDAO.getBuildingById( "HQ" );
    logger.debug( "Loaded building: " + building.getId() );
    logger.debug( "Loaded building: " + building.getName() );
}

The object is loaded without any problems. The log of the Primary-Key is no problem, too. But the log of the string attribute causes a LazyInitializationException. I understand why this happens. Hibernate returned a proxy with proxied atributes. When I want to access to a lazy-loaded attribute, Hibernate tried to load the value but the session is closed. Lazy loading is a great feature of Hibernate and I don't want to miss it.

Spring manages the session-handling for me. Spring opens a Hibernate session in the load-method of the Hibernate-template and closes the session after the method has finished.

But now I want to advise Spring that the session should be open in the whole method (runQuery()), which is shown above. I want to display some attributes of the object. I mentioned I can use a tranaction-manager of Spring to do that. So I use the Transactional-Annotation of Spring. But it doesn't work. Maybe my assumption to use the transaction-manager is wrong.

Has anybody an idea to advise Spring to hold open a session for the whole method?

+3  A: 

Are you sure Propagation.SUPPORTS is what you really need? That doesn't enforce the presense of a transaction, it just uses it if it's there. Some other part of the system needs to be starting and committing the transaction. If none is present, then the session may not be maintained as far as your log statements, resulting in a lazy load error.

Try using some other isolation level (like REQUIRED, or better yet, don't specify it all, and rely on the default) and see if that fixes your problem.

skaffman
+2  A: 

Changing to REQUIRED or just leaving it blank, will fix your problem. Propagation.SUPPORTS does not actually start a transaction... see http://www.ibm.com/developerworks/java/library/j-ts2.html

You probably haven't disabled non transactional reads (the default is enabled) and thus the read is successful, but the transaction for that read is only for the read itself (not for subsequent reads on the object returned).

The category for logging on transaction activity is org.springframework.transaction IIRC - if you turn this to DEBUG and you'll see details of when transactions are started and committed.

Michael Wiles
+1  A: 

Propagation.SUPPORTS means if there is a transaction present while the method is called then it will use the transaction else this block will execute without a transaction. You can change it to Propagation.REQUIRED which will always runs within a transaction. If there is already a transaction it will use that else this block will be executed in a new transaction block.

Arun P Johny
A: 

If you want RunQuery to run inside a Spring transaction, you have to get an object of the class defining it as a spring bean from the container. That way Spring can wrap transactional logic around the bean (And whatever methods it defines).
Defining that class as a spring bean will also save you from calling to (BuildingDAO)applContext.getBean("buildingDAO") because spring will wire the dao into your class (if you define it properly in the context xml or attributes).

Noam Gal