I'd like to display a table with all users of my database. The table should also contain the number of elemens of a list that is contained in the user object.
As I only need the size/count, I'd like to prevent fetching the whole list. Therefore I'm using LazyCollectionOption.EXTRA
, with the docs stating:
EXTRA = .size() and .contains() won't initialize the whole collection
But the following does still not work:
@Entity
class User {
@OneToMany
@LazyCollection(LazyCollectionOption.EXTRA)
List<Transaction> transactions;
}
When I call user.getTransactions().size()
, the result is:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: User.transactions, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575) at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214) at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155) at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:278)
Even if the @LazyCollection(LazyCollectionOption.EXTRA)
is not what's causing you trouble, if you care about application performance, you shouldn't be using EXTRA lazy fetching anyway.
I think you are fetching the Users list in a Hibernate Session, that gets closed before you want to access the User's transactions.
This is the typical scenario for LazyInitializationException, as the nested collections haven't been initialized when the Session was still opened.
Fetching all Users along with all their Transactions would get you a Cartesian Product which causes performance issues.
I think it's fine to get the Users list, and use a sub-select fetching for retrieving Transactions:
@OneToMany
@Fetch(FetchMode.SUBSELECT)
List<Transaction> transactions;
This will issue one more select to fetch all uninitialized transactions when you need them. This will require you to access at least one current attached User transactions to trigger the additional fetch of all uninitialized current attached transactions.
Otherwise you can use batch fetching:
@OneToMany
@BatchSize(size = 50)
List<Transaction> transactions;
Again this requires an opened Session and upon requesting a certain User.transactions, all User associated transactions will be initialized in batches of 50.
But since the @BatchSize is set for the collection only, as opposed to @Entity level, it means in the best case you will still need N additional queries for N currently attached Users. If each User has more than 50 transactions you will need more than N queries. This might perform worse than the sub-select fetching, which requires only one query (the original query is rerun using a sub-select to fetch all query related Users' transactions).