Hibernate, DAO pattern, and other related issues
I joined a team of developers that are putting together an app to help museum managers track the contents of the museums. The app uses Hibernate to deal with persistence. I've got lots of Java experience under my belt, but most of it was for apps with no persistence requirements, so this is the first time using Hibernate as well as my first time hearing about the DAO pattern.
Let me give a small scenario and then a question.
I have a Location class. A Location object represents a building, floor, or room (to keep it simple). Each Location (other than the topmost) has a parent location (a floor has a parent building, etc) as well as a set of children locations (a floor has many rooms). I have a custom display widget that renders the whole set of locations in a tree structure. For example...
Building X
|Floor 1
| |_Room 101
| |_Room 102
|
|Floor 2
|_Room 201
Here's the problem I'm running into. I want the set of children for each Location to be lazy loaded because the tree can actually be extremely large for many museums. I don't want EVERY Location immediately getting loaded the first time I reference one of them. When my renderers need to do their painting, they are constantly needing to find out if a Location has any children. In order to do this, I find myself putting Hibernate (or other persistence mech.) code into the renderers to make sure that the Location has an associated session before I try to reference the set of children. Otherwise the call to getChildren() throws a LazyLoadingException or something like that.
Here are my questions.
1. Is there a good way to hide the concept of persistence from my rendering code while still allowing for lazy loading of collections of objects?
2. How does the DAO pattern, which sounds like it addresses this, work along side of Hibernate?
3. Does the Proxy pattern help here? Could I just write wrappers around the Location objects that handle the session management code each time calls come into getChildren() or other such methods?
Message was edited by:
jds@ku.edu
> 1. Is there a good way to hide the concept of
> persistence from my rendering code while still
> allowing for lazy loading of collections of objects?
Yes, start with a DAO interface and have the Hibernate implementation behind that. The interface doesn't contain any notion of lazy loading. If you implement in JDBC, it's all eager.
> 2. How does the DAO pattern, which sounds like it
> addresses this, work along side of Hibernate?
DAO interface, Hibernate implementation:
package persistence;
public interface BuildingDao
{
Building findById(Long id);
List<Building> findAll();
List<Building> findByName(String name);
Building findByAddress(Address address);
void saveOrUpdate(Building b);
void delete(Long id);
void delete(Building b);
}
Have a Hibernate implementation of this. The Building will have a List of child Floors, which will have a List of child Rooms, etc.The Hibernate mappings will describe the lazy loading of Floor, Room children.
> 3. Does the Proxy pattern help here? Could I just
> write wrappers around the Location objects that
> handle the session management code each time calls
> come into getChildren() or other such methods?
No, have the UI elements call into a BuildingService. It holds onto session and transaction info. Hide that complication from UI/rendering layer. All it knows is "I want the Flatiron Building, NY, NY" - what does it care about lazy or eager loading?
%
I may have not communicated clearly about my class structure. I don't have distinct building, floor, and room classes. Instead I have a single class Location which has a field 'rank' that takes on values such as 0=building, 1=floor, and 2=room. I'm not sure that changes anything you've said, but just in case.
AND, the Location class is a Hibernate persistent class. In case that matters.
> I may have not communicated clearly about my class
> structure. I don't have distinct building, floor,
> and room classes.
Why not?
Instead I have a single class
> Location which has a field 'rank' that takes on
> values such as 0=building, 1=floor, and 2=room.
Location sounds like an enumeration then.
So in your model, if a Location has a List of child locations, what's to stop you from adding a floor to a room? Or a building to a floor?
> I'm
> not sure that changes anything you've said, but just
> in case.
>
> AND, the Location class is a Hibernate persistent
> class. In case that matters.
Actually, Hibernate doesn't require that a class know that it's being persisted, except in subtle ways (e.g., primary key and default constructor required, etc.)
Any rebuttals or things to add from you?
%
> > I may have not communicated clearly about my class
> > structure. I don't have distinct building, floor,
> > and room classes.
>
> Why not?
>
The reason for the single Location class instead of individual classes is extensibility. By making everything a location and having a rank, we can add more ranks later if a particular museum wants more granularity such as (campus, building, floor, wing, room, shelf, container). Instead of adding more database tables and more classes to our code, we just add more values of the rank field.
> Instead I have a single class
> > Location which has a field 'rank' that takes on
> > values such as 0=building, 1=floor, and 2=room.
>
> Location sounds like an enumeration then.
>
> So in your model, if a Location has a List of child
> locations, what's to stop you from adding a floor to
> a room? Or a building to a floor?
>
Nothing, other than business rules coded in the app, stops a user from making a room the parent of a building. We don't invision our users having direct SQL access to our databases.
> > I'm
> > not sure that changes anything you've said, but
> just
> > in case.
> >
> > AND, the Location class is a Hibernate persistent
> > class. In case that matters.
>
> Actually, Hibernate doesn't require that a class know
> that it's being persisted, except in subtle ways
> (e.g., primary key and default constructor required,
> etc.)
>
> Any rebuttals or things to add from you?
>
I hope I don't sound argumentative. I'm really just trying to learn. And for me, "arguing" back and forth on a topic usually sheds some light on underlying misconceptions I might have. I apologize if I've sounded belligerent.
I think a problem I've been having stems from my misunderstanding about how to use Hibernate. Our team had a preexisting DB that they generated classes to represent. I then thought that I shouldn't ever touch the code for those classes. I'm starting to think that if I really want a DAO pattern impl here, that I have to go into those classes and add CRUD functionality. Does this sound right?
> The reason for the single Location class instead of
> individual classes is extensibility. By making
> everything a location and having a rank, we can add
> more ranks later if a particular museum wants more
> granularity such as (campus, building, floor, wing,
> room, shelf, container). Instead of adding more
> database tables and more classes to our code, we just
> add more values of the rank field.
Doesn't sound very object-oriented to me. You can't add restrictions to what children are allowed that way. Why bother with an object-oriented language at all in this case?
The database must be a challenge with this. You have a type column. No restrictions on that, either.
> othing, other than business rules coded in the app,
> stops a user from making a room the parent of a
> building. We don't invision our users having direct
> SQL access to our databases.
Right, but what about developers? How will they know?
> > Any rebuttals or things to add from you?
> >
> I hope I don't sound argumentative.
Oh, you don't. I hope I don't, either. Just questioning, that's all.
> I'm really just trying to learn.
Me, too. 8)
> And for me, "arguing" back and
> forth on a topic usually sheds some light on
> underlying misconceptions I might have. I apologize
> if I've sounded belligerent.
No need for an apology. Your question is an excellent one.
So it's time for that class hierarchy and taking advantage of Hibernate's polymorphism.I think you can do it without having to add a table for each subtype. You can have one big table for the entire hierarchy or one per subclass - your choice.
Do think to see if there's any advantage to having a richer class hierarchy. If you talk about the problem you're modeling for a while you might find that it's useful.
%
Since you're ultimately tracking contents, there have to be leaf Contents in there somewhere. Do you want to have rules that say "You can put a Painting on a Wall in a Room, but not in a Hallway" or "Sculptures of a particular weight cannot be stored on a Floor with a capacity less than X times their weight and never on a Shelf" something like that.
Could be a great way to encapsulate business rules in objects.
%
We actually spent a lot of time deciding between the 1 table per tree level approach and the less OO approach of having a "type" column. We ultimately chose the later because we are using this same approach for another tree, a taxonomic one. The museums we are writing this for are museums full of various animal species (natural history museums). Taxonomic trees can have a large number of tables (usually around 17), many of which go unused by various institutions. Also, some institutions decide that they want new levels, for example, 'infratribe', in the taxonomy trees. Deciding that after deployment would require that we add a new table, generate a new class, and generate the Hibernate bindings between them. We didn't want to get into that.
You're right, this is a very messy database. I wish we were able to work with a cleaner schema. A lot of this DB schema is legacy stuff that we can't throw away. Such is life behind a keyboard.
> I think a problem I've been having stems from my
> misunderstanding about how to use Hibernate. Our
> team had a preexisting DB that they generated classes
> to represent. I then thought that I shouldn't ever
> touch the code for those classes. I'm starting to
> think that if I really want a DAO pattern impl here,
> that I have to go into those classes and add CRUD
> functionality. Does this sound right?
You can add more classes, you just can't add fewer. Hibernate can work with what they call composition.
A simple example is a Person object that has String attributes first and last names, street1, street2, city, state, and zip. You might have all these in a single persons table in your schema, but there's nothing to stop you from having a Person object with a Name child that encapsulates first and last name and an Address child that encapsulates street1, street2, city, state, and zip. This is Hibernate's composition in action.
Did they use Middlegen or Hibernate 3 tools to generate the Java objects from the schema?
Don't know if this helps you.
I feel your pain. Legacy schemas are all too common, and they aren't often optimized with objects in mind. (Or anything in mind.)
%
Your Location is one big Composite pattern from GoF. Is that how you modeled it?%
> > Why not?
> >
> The reason for the single Location class instead of
> individual classes is extensibility. By making
> everything a location and having a rank, we can add
> more ranks later if a particular museum wants more
> granularity such as (campus, building, floor, wing,
> room, shelf, container). Instead of adding more
> database tables and more classes to our code, we just
> add more values of the rank field.
>
This is how I would do it on the DAO level. So the database would have this flexibility. But on the business layer I would have hard classes. The database is harder to extend so that flexibility is good. But you don't really want to expose it this way on the presentation layer.
> So in your model, if a Location has a List of child
> locations, what's to stop you from adding a floor
> to
> a room? Or a building to a floor?
>
> othing, other than business rules coded in the app,
> stops a user from making a room the parent of a
> building. We don't invision our users having direct
> SQL access to our databases.
This is how I eventually implemented mine.
Message was edited by:
_dnoyeB
> Here's the problem I'm running into. I want the set
> of children for each Location to be lazy loaded
> because the tree can actually be extremely large for
> many museums. I don't want EVERY Location
> immediately getting loaded the first time I reference
> one of them. When my renderers need to do their
> painting, they are constantly needing to find out if
> a Location has any children...
Your renderers need to understand lazy questioning. The way I implment mine is that they don't ask for children until the '+' on the branch is pushed. This laziness is a tree behavior.
> Your renderers need to understand lazy questioning.
> The way I implment mine is that they don't ask for
> children until the '+' on the branch is pushed.
>This laziness is a tree behavior.
The "+" shouldn't even be rendered unless the renderer has already determined that the node has children. So, you pretty much have to ask for the child set before rendering the parent.
For me, its a sacrifice. I put the + there and it disappears when its pushed if no children. The point is really that its a node 'capable' of having children so it gets the '+'. If its not capable of having children it does not get it. So I don't have a problem with it being there and dissapearing. Its lazy so I don't know yet.
This is also tied to my suggestion of letting the database be loose but the classes structured. This way based on the class type itself you know if its capable of having children. Otherwise you are hosed because all of your classes can have children because you only have a single class. I still would put the + there.
Your main issue seems to be that you dont have methods on your business classes such as
#isLeaf
#hasChildren
In our situation, the isLeaf() and hasChildren() methods would require the node to ask for its children. We ABSOLUTELY CANNOT have structured classes here. We could ship with a class structure like Building, Floor, Room but then a user might decide that they want Building, Floor, Room, Shelf. We have to support this possibility. It happens ALL THE TIME for us. By not having a structured class hierarchy, we can support this user request without changing even a single line of code. We only have to do minor DB updates.
> In our situation, the isLeaf() and hasChildren()
> methods would require the node to ask for its
> children. We ABSOLUTELY CANNOT have structured
> classes here. We could ship with a class structure
> like Building, Floor, Room but then a user might
> decide that they want Building, Floor, Room, Shelf.
> We have to support this possibility. It happens ALL
> THE TIME for us. By not having a structured class
> hierarchy, we can support this user request without
> changing even a single line of code. We only have
> to do minor DB updates.
isLeaf and hasChildren should not request all the children. Fix your SQL. There is no dance around this.
Once that is fixed then you can have the generic container class that you want so bad without thrashing the database on each instance.
No need to get all hot and bothered about it. I have both Generic classes that yield their structure to the DB and others that apply their own structure. Changing the class vs. changing the db is really a wash. Though I prefer to change classes because I have more validation tools for code that db structure.
> > isLeaf and hasChildren should not request all the> children. Fix your SQL. There is no dance around> this.We haven't written any SQL. All DB access is done with Hibernate.
So Hibernate is generating the SQL for you. You can look at it, of course, and see how optimal it really is. If you find that it performs badly you can always substitute your own and use a named query.%
I wasn't aware that you could look at Hibernate generated XML. Where do I look for it? Can you give any basic steps on how to setup and use a named query for Hibernate queries?
> I wasn't aware that you could look at Hibernate
> generated XML.
You mean SQL?
> Where do I look for it?
I tell Spring that I'd like to have Hibernate show the SQL in the console by setting the appropriate property in the Hibernate config.
> Can you give
> any basic steps on how to setup and use a named query
> for Hibernate queries?
(1) Write a query.
(2) Give it a name.
(3) Provide the name of the query to your code so it can look it up.
The Hibernate docs are the best place to go for that.
%
> >
> > isLeaf and hasChildren should not request all the
> > children. Fix your SQL. There is no dance around
> > this.
>
> We haven't written any SQL. All DB access is done
> with Hibernate.
Whats the difference? You know what I'm talking about!
Fix your ORM. I use JDO and am completely familiar with this.In JDO I can declare fetch groups that will control how much of a particular Collection is fetched as well as how much of each class in the collection is initialized. I assume you are calling Collection#isEmpty or something similar and hibernate is loading all the children just to make this query?
In JDO I would declare on the child type, the fields that should be in the default fetch group. So the collection would indeedd load all the children, but it would probably just load the IDs for each child, but not initialize all of the childs fields.
How have you determined what Hibernate is doing? A log file?
> Fix your ORM. I use JDO and am completely familiar
> with this.In JDO I can declare fetch groups that
> will control how much of a particular Collection is
> fetched as well as how much of each class in the
> collection is initialized. I assume you are calling
> Collection#isEmpty or something similar and hibernate
> is loading all the children just to make this query?
I'm very new to Hibernate and the data layer in general. I have spent almost all of my 7 years of Java development working on desktop applications that deal with XML files, network sockets, etc. This is the first time I've ever used an ORM technology. I was completely unaware that the developer had any say in what Hibernate did as far as SQL queries. I didn't realize that I could possibly modify Hibernate's queries to better suit my purposes. I'll have to look into that.
The only reason I know that Hibernate is asking for the children is the LazyInstantiationException that gets thrown each time isEmtpy() is called on a parent bean.
If I wanted to use the DAO pattern for all access to Location objects, would I have something like...
public class LocationDao
{
..lots of code...
Set<Location> getChildren(Location loc);
}
In other words, would I simply make sure all callers use locDAO.getChildren(loc)
instead of something like loc.getChildren()
?
> If I wanted to use the DAO pattern for all access to
> Location objects, would I have something like...
>
> > public class LocationDao
> {
>..lots of code...
> et<Location> getChildren(Location loc);
> }
>
>
> In other words, would I simply make sure all callers
> use locDAO.getChildren(loc)
instead of
> something like loc.getChildren()
?
Assuming Location is a ValueObject I would say yes. The value object should be defined in the same package as the DAO interfaces. The only package that can use the DAO interfaces and value objects is the business methods package (and the DAO implementation). Therefore, there is no way for any other classes to make calls on Location because they can not see it. All they can see is the business methods package.
> Assuming Location is a ValueObject I would say yes.
By ValueObject, do you mean an immutable object without identity? (This is how Eric Evans defines it in "Domain Driven Design".)He cites the example of a Money object. If I needed a $10 USD Money object, a good way to model it would be to make it an immutable Value Object. One $10 USD Money object is a good as another. As a matter of fact, you and I would be able to share it safely because it's immutable.
> The value object should be defined in the same
> package as the DAO interfaces.
If we agree on the definition above I'd say Value Objects most certainly do not belong in the same package as the persistence interfaces. They belong in a package with all the other domain objects.
> The only package
> that can use the DAO interfaces and value objects is
> the business methods package (and the DAO
> implementation).
If Value Objects are immutable, there's no harm in using them elsewhere. They're safe in the web or UI tiers.
> Therefore, there is no way for any
> other classes to make calls on Location because they
> can not see it. All they can see is the business
> methods package.
What if the service layer is asked to return a List<Location>? What do you do then if no one else can see it?
I think the artificial limitation of restricting Value Objects this way is harmful if your solution for the UI and web tiers is to have a parallel object hierarchy that mirrors your Value Objects. That's a worse smell, IMO.
%
No, I just got my terms mixed up because I am not used to using them. Its a "Transfer Object."
> No, I just got my terms mixed up because I am not
> used to using them. Its a "Transfer Object."
No, I think Transfer Object has a special meaning: It's purpose in life is to ferry data between layers (e.g., to the UI layer and back). I usually see it as a parallel to a domain object hierarchy (e.g. model.Foo will have a dto.Foo twin, both with identical attributes). model.Foo may or may not be immutable; dto.Foo usually is.
Transfer Object is a Core J2EE pattern. It became a "best practice" when people found out that passing entity EJBs between layers was a bad idea.
I discourage their use. I think they're an unnecesssary maintenance burden, and I don't use entity EJBs.
%
OK. I don't use transfer objects either as I saw no added value.