Joshua Davis wrote:
Transfer Object is a design pattern frequently used with EJB:
http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObject.html
.
Your managed beans could copy the Hibernate-mapped objects (I call them
Persistent Domain Objects) into simple POJOs that aren't mapped. Leave
these in the session, stick them in a cache, store them in base64 encoded
hidden input fields, or whatever.
I guess I'm just not seeing the reason to use this with Hibernate. I
mean the whole point of Hibernate is to be able to persist you're POJO
and use them wherever you see fit and not have to worry about all the
complications that arise from EJBs. That's the ideal at least. The
reality is that you do have to worry about the Hibernate session and
loading objects and performing opertations within a session, but I'm
having a hard time buying into the idea of using transfer objects with
Hibernate.
That's the
first time I've ever heard anyone mention that. The problem
with telling Hibernate to initialize the object graph is
that, in most cases I think, the use of Hibernate is
abstracted away from the webapp layer. So your backing beans
don't really know they need to do that and even if they did
they couldn't.
The DAO should do it. The backing beans (business session pattern) can ask
the DAO to do it in an abstract way.
I'm guessing you're referring to something like having a secondary
parameter so it looks something like myDao.get (oid, lazyLoad) or
something like that. To me that's still bad because you need to know
something about the way objects are stored at a very high level. In
reality, the backing beans probably don't even access the DAOs directly,
but do it via some middle tier service object.
And setting the lazy attribute to false is a
workaround, but could lead to large graphs being loaded into
many users HTTP sessions.
This works for simple graphs only. For complex graphs, your DAO (which is
the only thing in your system that should import Hibernate) can pick and
chose which objects to initialize. What this is doing is essentially
turning your PDOs into Transfer Objects.
The problem with have the DAOs pick and choose which objects to
initialize is that it assumes knowledge of the context in which they
will be used. I mean, how can you know apriori what you need to
initialize in a get(oid) method? You have no idea where it will be used.
Reassociating the object with the session has a similar
problem as trying to initialize the object from within the
webapp layer before putting it in the session. Your webapp
simply does not know that you are using Hibernate. And even
if it did, it has no access to the actual Hibernate session.
I toyed around at one time with the idea of trying to create
a servlet filter that would automatically reassociate
anything in the HTTP session with the Hibernate session.
There are a couple of problems with that tho. First off,
it's not always easy to determine what objects are Hibernate
persisted objects and which aren't. Second, if the actual
Hibernate object is wrapped in a backing bean that is
actually put in the HTTP session simply scanning objects in
the HTTP session won't work. Finally, what convinced me it
was a bad idea is that the only ways to reassociate an object
with a Hibernate session that I was able to find is to use
merge(), update(), or lock(). The first two will update the
database with the object you provide, and locking requires
the underlying data in the db hasn't changed. So you'd lose
any changes that could have occured while the object was in
the session.
Yikes! That sounds complicated. ;)
The only reason you would want to reassociate the objects is if you want to
update them. If you are just using the session as a cache, then these
objects should not be mapped. That will cause all kinds of headaches.
But there is at least one situation I can think of where it makes sense
and that is a users profile. The situation that I was running into was
that a users profile had the standard contact information, but then had
links to other domain objects the user was responsible for. What I
would up having to do is separate all that out from the user profile and
create a separate myDao.get(curerntUser) kind of thing, which is what I
was getting at before. Maybe this is the right way to do it, but it
also seems to kind of make sense to be able to simply say
currentUser.getMyStuff(), you know what I mean?
So, like I said, just refetching the Hibernate objects for
each request is much easier and doesn't cause too much extra
load on the database if your using the 2nd level cache.
Bingo! You've got it there. To me, it makes sense to use Transfer Objects,
or at least make the integration layer (where the DAOs live) able to provide
initialized sub-graphs - mapped objects turned into Transfer Objects.
Which brings me back to JSF... My current JSF application uses DAOs that
provide the mapped objects to the business beans. These beans create
transfer objects that are accessed by the view. When actions that require
db modification occur, the DAO is used to look up the object again and the
new values are set into the mapped object from the transfer object. This is
a little bit more programming if you have a separate class for the mapped
object and the transfer object, but it's not much more work at all if you
use the initialized subgraph concept. The benefit is that there is no need
for any fancy session reassociation or anything like that. The
presentation layer and the persistence (integration) layer are completely
separated.
Seems much more complicated than I thought using Hibernate was supposed
to be. I mean, like I said before, I thought the idea of Hibernate was
to take any plain old java object and be able to make it persitent
without all this extra overhead. I'm also having a hard time
envisioning what these transfer objects would look like, I mean are they
just copies of the Hibernate objects? If so, how is this all that
different from just recursively doing initialize() on persitent objects?
Rich
P.S. Sorry if I seem argumentative but I'm actually just getting done
with my first project that uses JSF + Spring + Hibernate and the
LazyInitializationException was something I ran into with my user
profile objects that bothered me for days before I just said, "Ok,
fine. I'll do a myDao.getStuffForUser(currentUser)" method rather than
being able to use something simpler like currentUser.getStuff()." So
this is something I'm keenly interested in finding how other people
approach the problem.