views:

458

answers:

3

Correct me if anything is wrong.

Now when we use Spring DAO for ORM templates, when we use @Transactional attribute, we do not have control over the transaction and/or session when the method is called externally, not within the method.

Lazy loading saves resources - less queries to the db, less memory to keep all the collections fetched in the app memory.

So, if lazy=false, then everything is fetched, all associated collections, that is not effectively, if there are 10,000 records in a linked set.

Now, I have a method in a DAO class that is supposed to return me a User object. It has collections that represent linked tables of the database. I need to get a object by id and then query its collections.

Hibernate "failed to lazily initialize a collection" exception occurs when I try to access the linked collection that this DAO method returns.

Explain please, what is a workaround here?

Update: All right, let me ask you this. DAO is an abstract layer, so a method "getUserById(Integer id)" is supposed to return an Object.

What if in some cases I need these linked collections of the User object and in other situation I need those collections.

Are there only two ways: 1) lazy loading = false 2) create different methods: getUserByIdWithTheseCollections(), getUserByIdWithOtherCollections() and inside those methods use your approach?

I mean are there only 2 ways and nothing better?

Update 2: Explain please, what would give me the explicit use of SESSIONFACTORY? How does it look in practice? We create an instance of DAO object, then inject it with session factory and this would mean that two consequent method calls to DAO will run within the same transaction? It seems to me that anyway, DAO is detached from the classes that make use of it!

The logic and transactions are encapsulated within DAO, right?

+2  A: 

You can get the linked collection in transaction to load it while you're still within the transaction:

User user = sessionFactory.getCurrentSession().get(User.class, userId);
user.getLinkedCollection().size();
return user;

As BalusC has pointed out, you can use Hibernate.initialize() instead of size(). That's a lot cleaner.

Then when you return such an entity, the lazy field is already initialized.

Replying to your PS - is using transactions on service level (rather than DAO) level feasible? It seems to be, as doing each DAO call in separate transaction seems to be a waste (and may be incorrect).

Konrad Garus
@Konrad Garus Please see my postscript of the question, here the text is less readable, so I ask you there.
EugeneP
@EugeneP See updated answer.
Konrad Garus
+4  A: 

You need to access it in the same Hibernate session. If this is not doable, then you need to Hibernate#initialize() the collection during query. Also see chapter 19.1.4 of the Hibernate documentation.

Update: since you're using Spring, I bet that you're using a web application. In that case, I'd recommend to use the "open session in view" approach with Spring's OpenSessionInViewFilter.

BalusC
@BalusC Please see my updated question (pps)
EugeneP
Transactions lies outside DAO control. Transactions are to be controlled by your service/business layer. You normally start a transaction and then call one or more DAO methods (which may be related to each other that way that you'd like to rollback everything whenever a step fails) and then commit the transaction. In webapplications it's an acceptable practice to start the transaction in the beginning of the HTTP request and commit it when the HTTP response is been sent. The DAO just have to get the connection from the currently available/open transaction.
BalusC
+2  A: 

I find that it's best to put @Transactional at the service layer, rather than the DAO layer. Otherwise, all your DAO calls are in separate hibernate sessions - all that object equality stuff won't work.

John