刚使用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(1888d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(2888d);
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(1888d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(2888d);
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(1888d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(2888d);
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(1888d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(2888d);
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(1888d);
o1.setAddress("Beijing");
Order o2 = new Order();
o2.setPrice(2888d);
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