kapok

垃圾桶,嘿嘿,我藏的这么深你们还能找到啊,真牛!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  455 随笔 :: 0 文章 :: 76 评论 :: 0 Trackbacks

http://www.hibernate.org/209.html

Some Parent-Child Principles

Getting Parents and Children to Play Nice

If you browse the Hibernate Discussion forum http://forum.hibernate.org you'll notice that about 50% of the questions are answered by RTFM, referencing the Parent/Child example in chapter 16 of the Hibernate Reference http://www.hibernate.org/hib_docs/reference/en/html.

In addition, here are some of the basic principles and pitfalls I've discovered, along with an example. Corrections are most welcome ... this is a repost from our internal blog

First ... the situation. Let's use two classes called ParentClass and jobs as an example

We have (pseudocode):

ParentClass {
     Set jobs;
}

as our parent, and:

ChildJobClass implements GenericJob {
     ParentClass parent;
}

as our child.

One context can have zero to many jobs, hence the set of jobs. One notification has but one parent.

PRINCIPLE 1: Your job is to set up native object references

1. All the references in these classes are object references. The most common place to go wrong is trying to manage object references as pointers or integers in your code. Don't do that! Translating the object references into serialized pointers is Hibernate's job. Your job is to make sure the native Java structures are correct on an object level, e.g. that ParentClass contains a real live set of ChildJobClass objects, not their ID's, and that ChildJobClass objects have a real live parent object which is an instance of the ParentClass class.

2. Next, keep in your mind the idea that, while you do not manage the relational mapping of the object references, you do need to manage the object reference itself. You must go beyond declaring ParentClass parent and actually make a call to *ChildJobClass.setParent(myParentClass) * in your code. This is mostly done at construction time, so you'll probably have a constructor like this:

    public ChildJobClass (ParentClass thisParent)
    {
        setParent(thisParent);
    }

Note that the class that is constructing the notification is responsible for passing in a valid parent ParentClass to the constructor. Note also that the constructor itself is responsible for assigning that parent ParentClass to the parent property of the ChildJobClass. Fail either step and you'll get "cannot insert" or not null integrity constraint problems.

Principle 2: Hibernate's job is to read *.hbm.xml maps and translate your object references into crossreferences in the database

This means that your code does not (at the risk of being redundant) mess around with integer values representing object identifiers. Each of your mapped objects will have an identifier, complete with getter and setter, but unless you move into special features of the id mapping (and you should not!), you do not actually call those setters and getters. Hibernate does.

All your help to Hibernate comes in the area of mapping, e.g. *.hbm.xml files. That's where you inform hibernate of the structure of your objects and how that structure maps to database tables and columns

Principle 3: Most parent-child relationships should be mapped as bidirectional

Generally speaking you'll want parent objects to have their kids in place when constructed and child objects to have their parents when constructed. If you're concerned about overhead you can defer the construction of the references to the moment they are needed using lazy initialization, outside the scope of this note. The most common form of bidirectional parent child mapping is the Basic Collection pattern, which maps one parent to many children, many children to one parent. You can also do many-to-many on the child side -- see the excellent Index of Relationships http://www.xylax.net/hibernate/ page for examples. We'll deal with basic here.

In the Basic Collection pattern the mapping works like this:

1. The parent maps the set containing the children to the appropriate child table as a one-to-many relationship, e.g. one parent to many children.

2. The parent marks the relationship as "inverse". This attribute says that the parent doesn't actually update the relationship; the child updates the relationship. We do this so that we can deal with "NOT NULL" constraints on the child side.

So far our parent-side mapping looks like this:

 <set name="jobs" table="GENERIC_JOB" lazy="false" cascade="all" inverse="true" >
   <key column="PARENT_ID"/>
   <one-to-many class="org.mitretek.MyApplication.workflow.job.ChildJobClass"/>
 </set>

Which says:

  • The property of the parent object is called jobs, and is a set class
  • The objects in the jobs set are stored in the GENERIC_JOB table
  • Hibernate uses the PARENT_ID column of the GENERIC_JOB table to store the identifier of the parent object
  • The class to be stored in the GENERIC_JOB table is the ChildJobClass class

The class property might give you pause ... you might expect a GenericJob class. Ordinarily you'd be right; in our specific example GenericJob is really an interface that ChildJobClass and a few other classes implement, and you're looking at a polymorphic persistence situation. We'll address that later. Pretend you entered the ChildJobClass table if you want.

3. On the child end, map the child back to the parent using a many-to-one relationship. This relationship actually manages the link to and from parent.

    <many-to-one name="parent" class="org.mitretek.MyApplication.workflow.ParentClass" column="parent_id" not-null="true" />

There are a few things to note in this simple snip of mapping:

  • The many-to-one mapping is instead of a <property .../> mapping for the parent property, not along with!
  • The name attribute lists the name of the property of the child where the parent is inserted. As we said earlier, this is done by getters and setters on the child, and a genuine Java property of the appropriate parent class in the child object. Be sure to put a parent object in the property at construction or another appropriate time before persistance
  • The parent_id column must exist in the child table. The child table isn't referenced because the class that is being mapped (it happens to be in GenericJob.hbm.xml) has already indicated tables.
  • Not null is enforced here.

That's pretty much it. At persistance time, assuming you're persisting a ParentClass with a few ChildJobClasss nested in a set property the following activities happen (not necessarily in order):

  • Hibernate notices from the parent mapping that the jobs need to be mapped to the GENERIC_JOB table using the PARENT_ID column of GENERIC_JOB, but that the actual maintenance of the insert is done from the child end.
  • Hibernate notices from the child mapping that the object being mapped to the GENERIC_JOB table is called this.parent, and that its ID indeed goes into the PARENT_ID column, and furthermore it must not be null
  • Hibernate transacts enough SQL to get identifiers for any not-yet-persisted objects. This is the big reason you don't mess directly with identifiers.
  • Hibernate persists the parent and automatically persists its kids in the same transaction, being sure to set the parent's identifier in the parent_id field of the child so that it can be reconstituted in a future query.

Principle 4: Deal with Polymorphic Persistence from the superclass and the subclass will take care of itself

We mentioned earlier that GenericJob was really an interface that ChildJobClass and a bunch of other classes implement for different job types. We used the table-per-subclass mapping strategy for this, which is outside the scope of this note, except to point out that the strategy implements as a join from a superclass GENERIC_JOB table which contains the properties of the interface or top level class, to each subclass table, such as NOTIF_JOB for notification jobs.

The point here is that you do not deal directly with the subclass tables -- that too is Hibernate's job. Deal with the top-level table but indicate which underlying class is being persisted. Hibernate will extend the record across the join automatically.

For that reason we mapped our notification to the GENERIC_JOB table but indicated it was implemented as a ChildJobClass.

Hibernate stored the interface attributes in GENERIC_JOB and the child-specific nonInterface attributes in the NOTIF_JOB table.


  NEW COMMENT

Fills in the gaps
01 Dec 2004, 11:56
mungo@knotwise
Maybe you'd eliminate 50% of the RTFMs if you'd put this content into
the reference manual. This fills in lots of gaps.
 
Physical RI on tables
16 Feb 2005, 16:10
rpruthee
I am trying to do an insert in the parent and child with no 
Referrential integrity. Hibernated inserts a row in the parent table 
and in the child but it puts 0 in the FK column in the child table. I 
have checked eveything and I could not find any problems with the code 
and the mapping.

Also when I try to insert multiple child rows in the db, Hibernate 
throws NonUniqueObjectException.

Any ideas? Is it because no physical FK constraints have been defined.
Thanks.
 
posted on 2005-05-24 20:56 笨笨 阅读(552) 评论(0)  编辑  收藏 所属分类: J2EEHibernateAndSpringALL

只有注册用户登录后才能发表评论。


网站导航: