剛使用Hibernate的時候經常被各種關系關聯映射弄得頭大, 在這裡來總結一些單向關聯映射和雙向關聯映射案例.
單向關聯( Unidirectional associations )
多對一( many-to-one )
單向many-to-one關聯是最常見的單項關聯關系, 這裡使用Customer和Order為例, Customer為一的一方, Order為多的一方
Order配置 :
<class name="Order" table="u_order" catalog="hibernateTest_1">
<id name="id" column="o_id" >
<generator class="native"></generator>
</id>
<property name="price" column="o_price"></property>
<property name="address" column="address"></property>
<many-to-one name="customer" class="Customer" column="c_id" cascade="save-update"/>
</class>
Customer配置:
<class name="Customer" table="u_customer" catalog="hibernateTest_1">
<id name="id" column="c_id">
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
</class>
測試代碼:這裡要注意的是我在Order中配置了
cascade="save-update"
,是以在代碼中Order單向關聯Customer, 通過隻儲存Order來儲存完整資料. 之後的代碼也會采用同樣的方法, 就不再贅述了
Order o1 = new Order();
o1.setPrice(d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(d);
o2.setAddress("NewYork");
Customer c = new Customer();
c.setName("Tom");
o1.setCustomer(c);
o2.setCustomer(c);
session.save(o1);
session.save(o2);
一對多( one-to-many )
同樣使用Customer和Order為例, 這裡用Customer關聯Order. 但是基于外鍵關聯的單向一對多關聯是一種很少見的情況,我們不推薦使用它.
Customer配置:
<class name="Customer" table="u_customer" catalog="hibernateTest_1">
<id name="id" column="c_id">
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
<set name="orders" cascade="save-update">
<key column="c_id"/>
<one-to-many class="Order"/>
</set>
</class>
Order配置:
<class name="Order" table="u_order" catalog="hibernateTest_1">
<id name="id" column="o_id" >
<generator class="native"></generator>
</id>
<property name="price" column="o_price"></property>
<property name="address" column="address"></property>
</class>
測試代碼 :
Order o1 = new Order();
o1.setPrice(d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(d);
o2.setAddress("NewYork");
Customer c = new Customer();
c.setName("Tom");
c.getOrders().add(o1);
c.getOrders().add(o2);
session.save(c);
一對一( one-to-one )
一對一關聯有兩種情況, 基于外鍵和基于主鍵:
基于外鍵的一對一關聯
基于外鍵關聯的單向一對一關聯和單向多對一關聯幾乎是一樣的. 唯一的不同就是單向一對一關聯中的外鍵字段具有唯一性限制. 這裡使用Person和IDcard, 用Person來關聯IDcard:
Person配置:
<class name="Person" table="u_person" catalog="hibernateTest_1">
<id name="id" column="p_id">
<generator class="native"></generator>
</id>
<property name="name" column="p_name"></property>
<many-to-one name="idcard" class="IDcard" column="i_id" cascade="save-update"/>
</class>
IDcard配置:
<class name="IDcard" table="u_idcard" catalog="hibernateTest_1">
<id name="id" column="i_id">
<generator class="native"></generator>
</id>
<property name="idnum" column="i_idnum"></property>
</class>
測試代碼:
Person p = new Person();
p.setName("jack");
IDcard i = new IDcard();
i.setIdnum("500201199111112345");
p.setIdcard(i);
session.save(p);
基于主鍵的一對一關聯
這裡我使用Husband-Wife來說明
Wife配置:
<class name="Wife" table="u_wife" catalog="hibernateTest_1">
<id name="id" column="w_id" >
<generator class="foreign">
<param name="property">husband</param>
</generator>
</id>
<property name="name" column="w_name"></property>
<one-to-one name="husband" cascade="save-update"/>
</class>
Husband配置:
<class name="Husband" table="u_husband" catalog="hibernateTest_1">
<id name="id" column="h_id" >
<generator class="native"></generator>
</id>
<property name="name" column="h_name"></property>
</class>
測試代碼:
Husband h = new Husband();
h.setName("LiLei");
Wife w = new Wife();
w.setName("HanMeimei");
w.setHusband(h);
session.save(w);
使用連接配接表的單向關聯(Unidirectional associations with join tables)
多對一(many-to-one)
Order配置
<class name="Order" table="u_order" catalog="hibernateTest_1">
<id name="id" column="o_id" >
<generator class="native"></generator>
</id>
<property name="price" column="o_price"></property>
<property name="address" column="address"></property>
<join table="customer_order">
<key column="o_id"/>
<many-to-one name="customer" class="Customer" column="c_id" cascade="save-update"/>
</join>
</class>
Customer配置
<class name="Customer" table="u_customer" catalog="hibernateTest_1">
<id name="id" column="c_id">
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
</class>
測試代碼:
Order o1 = new Order();
o1.setPrice(d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(d);
o2.setAddress("NewYork");
Customer c = new Customer();
c.setName("Tom");
o1.setCustomer(c);
o2.setCustomer(c);
session.save(o1);
session.save(o2);
一對多 ( one-to-many )
我們應該優先采用基于連接配接表的單向一對多關聯. 通過指定
unique="true"
,我們可以把多對多關聯關系變為一對多關聯關系
Customer配置:
<class name="Customer" table="u_customer" catalog="hibernateTest_1">
<id name="id" column="c_id">
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
<set name="orders" cascade="save-update" table="customer_order">
<key column="c_id"/>
<many-to-many class="Order" column="o_id"/>
</set>
</class>
Order配置:
<class name="Order" table="u_order" catalog="hibernateTest_1">
<id name="id" column="o_id" >
<generator class="native"></generator>
</id>
<property name="price" column="o_price"></property>
<property name="address" column="address"></property>
</class>
測試代碼:
Order o1 = new Order();
o1.setPrice(d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(d);
o2.setAddress("NewYork");
Customer c = new Customer();
c.setName("Tom");
c.getOrders().add(o1);
c.getOrders().add(o2);
session.save(c);
一對一( one-to-one )
基于連接配接表的單向一對一關聯非常少見, 是以這裡不讨論.
多對多( many-to-many )
這裡使用Student-Course模型
Student配置:
<class name="Student" table="u_student" catalog="hibernateTest_1">
<id name="id" column="s_id" >
<generator class="native"></generator>
</id>
<property name="name" column="s_name"></property>
<set name="courses" cascade="save-update" table="student_course">
<key column="s_id"/>
<many-to-many class="Course" column="c_id"/>
</set>
</class>
Course配置:
<class name="Course" table="u_course" catalog="hibernateTest_1">
<id name="id" column="c_id" >
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
</class>
測試代碼:
Student s1 = new Student();
s1.setName("Tom");
Student s2 = new Student();
s2.setName("Jerry");
Course c1 = new Course();
c1.setName("math");
Course c2 = new Course();
c2.setName("physics");
s1.getCourses().add(c1);
s1.getCourses().add(c2);
s2.getCourses().add(c1);
s2.getCourses().add(c2);
session.save(s1);
session.save(s2);
雙向關聯(Bidirectional associations)
雙向關聯和單向關聯的測試代碼部分相同,這裡不再貼出測試代碼
一對多(one to many)/多對一(many to one)
雙向多對一關聯 是最常見的關聯關系. 下面的例子解釋了這種标準的父/子關聯關系.
<class name="Order" table="b_order" catalog="hibernateTest">
<id name="id" column="o_id" >
<generator class="native"></generator>
</id>
<property name="price" column="price"></property>
<property name="address" column="address"></property>
<many-to-one name="customer" class="Customer" column="c_id"/>
</class>
<class name="Customer" table="b_customer" catalog="hibernateTest">
<id name="id" column="c_id">
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
<set name="orders" inverse="true" cascade="save-update">
<key column="c_id"/>
<one-to-many class="Order"/>
</set>
</class>
一對一(One-to-one)
基于外鍵的一對一雙向關聯
<class name="Person" table="b_person" catalog="hibernateTest_1">
<id name="id" column="p_id">
<generator class="native"></generator>
</id>
<property name="name" column="p_name"></property>
<many-to-one name="idcard" class="IDcard" column="i_id" cascade="save-update"/>
</class>
<class name="IDcard" table="b_idcard" catalog="hibernateTest_1">
<id name="id" column="i_id">
<generator class="native"></generator>
</id>
<property name="idnum" column="i_idnum"></property>
<one-to-one name="person" class="Person" property-ref="idcard"/>
</class>
基于主鍵的一對一關聯關系
<class name="Husband" table="b_husband" catalog="hibernateTest_1">
<id name="id" column="h_id" >
<generator class="native"></generator>
</id>
<property name="name" column="h_name"></property>
<one-to-one name="wife" cascade="save-update"/>
</class>
<class name="Wife" table="b_wife" catalog="hibernateTest_1">
<id name="id" column="w_id" >
<generator class="foreign">
<param name="property">husband</param>
</generator>
</id>
<property name="name" column="w_name"></property>
<one-to-one name="husband" cascade="save-update"/>
</class>
這裡的測試代碼與單向關聯時有點不一樣, 是以貼出來, 由于
generator class="foreign"
配置在Wife的配置檔案中, 是以在儲存Husband進行級聯儲存之前,需要調用
h.setWife(w);
w.setHusband(h);
這兩行代碼來維護外鍵, 比單向關聯多一步操作, 當然, 如果調用
session.save(w);
進行級聯儲存的話, 也無需調用
h.setWife(w);
維護外鍵.
Husband h = new Husband();
h.setName("Lilei");
Wife w = new Wife();
w.setName("Hanmeimei");
h.setWife(w);
w.setHusband(h);
session.save(h);
使用連接配接表的雙向關聯(Bidirectional associations with join tables)
一對多(one to many)/多對一(many to one)
<class name="Customer" table="b_customer" catalog="hibernateTest_1">
<id name="id" column="c_id">
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
<set name="orders" cascade="save-update" table="_customer_order">
<key column="c_id"/>
<many-to-many class="Order" column="o_id" />
</set>
</class>
需要注意的是這種情況下,
inverse="true"
可以配置在Collection或者join任意一邊, 同時Java代碼中也需要對主鍵進行維護.
<class name="Order" table="b_order" catalog="hibernateTest_1">
<id name="id" column="o_id" >
<generator class="native"></generator>
</id>
<property name="price" column="o_price"></property>
<property name="address" column="address"></property>
<join table="_customer_order" inverse="true">
<key column="o_id"/>
<many-to-one name="customer" column="c_id" cascade="save-update"/>
</join>
</class>
測試代碼:
Order o1 = new Order();
o1.setPrice(d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(d);
o2.setAddress("NewYork");
Customer c = new Customer();
c.setName("Tom");
o1.setCustomer(c);
o2.setCustomer(c);
c.getOrders().add(o1);
c.getOrders().add(o2);
session.save(c);
多對多(many-to-many)
<class name="Course" table="u_course" catalog="hibernateTest_1">
<id name="id" column="c_id" >
<generator class="native"></generator>
</id>
<property name="name" column="c_name"></property>
<set name="students" cascade="save-update" table="student_course" inverse="true">
<key column="c_id"/>
<many-to-many class="Student" column="s_id"/>
</set>
</class>
<class name="Student" table="u_student" catalog="hibernateTest_1">
<id name="id" column="s_id" >
<generator class="native"></generator>
</id>
<property name="name" column="s_name"></property>
<set name="courses" cascade="save-update" table="student_course">
<key column="s_id"/>
<many-to-many class="Course" column="c_id"/>
</set>
</class>
總結 : 通過上面的例子, 其實可以看出, 在配置中進行雙向關聯, 在操作Java代碼時更加靈活, 可以操作任意一方. 是以, 我傾向于在配置中進行雙向關聯, Java代碼中級聯操作(減少資源浪費). 需要注意的是, 在一些情況下,進行級聯操作時, 需要對主鍵進行維護.
參考文獻:HIBERNATE - Relational Persistence for Idiomatic Java 1 Hibernate Reference Documentation 3.6.10.Final by Gavin King、Christian Bauer、Max Rydahl Andersen、Emmanuel Bernard、Steve Ebersole and Hardy Ferentschik