天天看點

Hibernate關聯關系映射單向關聯( Unidirectional associations )使用連接配接表的單向關聯(Unidirectional associations with join tables)雙向關聯(Bidirectional associations)使用連接配接表的雙向關聯(Bidirectional associations with join tables)

剛使用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