I tend to use Hibernate in combination with Spring framework and it's declarative transaction demarcation capabilities (e.g., @Transactional).
As we all known, hibernate tries to be as non-invasive and as transparent as possible, however this proves a bit more challenging when employing lazy-loaded
relationships.
I see a number of design alternatives with different levels of transparency.
- Make relationships not lazy-loaded (e.g.,
fetchType=FetchType.EAGER)
- This vioalites the entire idea of lazy loading ..
- Initialize collections using
Hibernate.initialize(proxyObj);
- This implies relatively high-coupling to the DAO
- Although we can define an interface with
initialize
, other implementations are not guaranteed to provide any equivalent.
- Add transaction behaviour to the persistent
Model
objects themselves (using either dynamic proxy or@Transactional
)- I've not tried the dynamic proxy approach, although I never seemed to get @Transactional working on the persistent objects themselves. Probably due to that hibernate is operation on a proxy to bein with.
- Loss of control when transactions are actually taking place
- Provide both lazy/non-lazy API, e.g,
loadData()
andloadDataWithDeps()
- Forces the application to know when to employ which routine, again tight coupling
- Method overflow,
loadDataWithA()
, ....,loadDataWithX()
- Force lookup for dependencies, e.g., by only providing
byId()
operations- Requires alot of non-object oriented routines, e.g.,
findZzzById(zid)
, and thengetYyyIds(zid)
instead ofz.getY()
- It can be useful to fetch each object in a collection one-by-one if there's a large processing overhead between the transactions.
- Requires alot of non-object oriented routines, e.g.,
- Make part of the application @Transactional instead of only the DAO
- Possible considerations of nested transactions
- Requires routines adapted for transaction management (e.g., suffiently small)
- Small programmatic impact, although might result in large transactions
- Provide the DAO with dynamic fetch profiles, e.g.,
loadData(id, fetchProfile);
- Applications must know which profile to use when
- AoP type of transactions, e.g., intercept operations and perform transactions when necessary
- Requires byte-code manipulation or proxy usage
- Loss of control when transactions are performed
- Black magic, as always :)
Did I miss any option?
Which is your preferred approach when trying to minimize the impact of lazy-loaded
relationships in your application design?
(Oh, and sorry for WoT)
I would say the initial assumption is wrong. Transaparent persistence is a myth, since application always should take care of entity lifecycle and of size of object graph being loaded.
Note that Hibernate can't read thoughts, therefore if you know that you need a particular set of dependencies for a particular operation, you need to express your intentions to Hibernate somehow.
From this point of view, solutions that express these intentions explicitly (namely, 2, 4 and 7) look reasonable and don't suffer from the lack of transparency.