一.简介
在进行关系型数据库设计时,表与表之间的关系往往不是独立的,而是相互关联的,这就是所谓的多表设计。一般来说,数据库表与表包含以下几种关系。

建表原则
一对一:主键对应,一方的主键作为另一方的主键。
一对多:在多的一方创建外键指向一的一方的主键
多对多:创建一个中间表,中间表的最少两个字段作为外键分别指向多对多双方的主键。
此种关系在hibernate中用Java对象表示如下:
class A class A class A
B b; Set<B> bs; Set<B> bs
} } }
class B class B class B
A a; Set<A> as
A a; } }
}
一对一的关系 一对多的关系 多对多的关系
在一般情况下,一对多的情况是比较常见的,比如学生与老师的关系,部门与员工的关系,客户与联系人的关系,所以,下面将以一对多关系为例说明hibernate是怎么建立多表之间的关系的。
二.hibernate的一对多关系映射操作实例
1.例子说明:
本实例建立两个表,分别是联系人表和客户表(典型的客户管理系统中经常遇到的),其中一个客户拥有多个联系人,即客户与联系人的关系为一对多。所以,联系人中存在外键,该外键指向客户。具体描述如下。
2.创建hibernate实体
联系人实体
private Long lkm_id;
private Character lkm_gender;
private String lkm_name;
private String lkm_phone;
private String lkm_email;
private String lkm_qq;
private String lkm_mobile;
private String lkm_memo;
private String lkm_position;
//注意这里是关键写法,在多的一方的实体中创建一个类型为一的一方的属性
private Customer customer ;
get方法....
set方法....
}
客户实体
public class Customer {
private Long cust_id;
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
//注意这里是关键,在一的一方的实体中创建一个泛型类型为多的一方的set集合。
private Set<LinkMan> linkMens = new HashSet<LinkMan>();
}
2.创建映射关系
联系人映射关系的写法(多的一方)
前面普通字段不再累述,可参见笔者前一篇博客。重点讲述关联对象的配置。一的一方使用many-to-one元素。
<many-to-one name="customer" column="lkm_cust_id" class="Customer的完整类名" >
</many-to-one>
name属性:引用属性名
column属性: 外键列名
class属性: 与我关联的对象完整类名
客户映射关系的写法(一的一方)
多的一方使用set元素来描述被映射类的set集合,
<set name="linkMens" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan的完整类名" />
</set>
key标签的column属性:用来描述对应文件多的一方的主键名称。
one-to-many元素 的class属性:用来描述映射关联类
3.将映射添加到hibernate主配置文件中
<mapping resource="Customer.hbm.xml文件的包路径" />
<mapping resource="LinkMan.hbm.xml的包路径" />
4.编写测试函数
public void text(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 开启事务
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//创建一个客户
Customer c = new Customer();
c.setCust_name("谷歌");
//创建多个联系人
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("crc");
LinkMan lm2 = new LinkMan();
lm2.setLkm_name("psl");
//表达一对多,客户下有多个联系人
c.getLinkMens().add(lm1);
c.getLinkMens().add(lm2);
//表达对对对,联系人属于哪个客户
lm1.setCustomer(c);
lm2.setCustomer(c);
//若无设置级联操作,则所有对象均要保存,否则出现瞬时对象异常。
session.save(c);
session.save(lm1);
session.save(lm2);
//4提交事务
tx.commit();
//5关闭资源
session.close();
}
若制定Junit测试后为绿条,且数据库新创建两个表,说明hibernate执行多表关联操作成功。
三.hibernate多表关系映射的级联操作
所谓级联操作是指当主控方执行保存,更新,删除操作时,其关联的对象也会执行相同的操作。在映射文件中通过set元素的
属性cascade来配置。主控方的选择是任意的,但一般习惯主控方选择为一的一方。
1.级联保存,关键词为save-update
配置如下代码:
<set name="linkMens" cascade="save-update" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
此时,只要执行保存客户,联系人会跟着保存下来。
2.级联删除,关键词为delete
配置代码如下
<set name="linkMens" cascade="delete" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
这时,在代码中删除了客户,与其关联的联系人也会跟着删除,但是,要注意,这种操作比较危险,一般不用。
要注意,级联保存和级联删除是可以同时配置 的。
四.双向关联产生多余的SQL语句 的问题
在实行表对象关联时操作时,SQL语句有可能会重复执行,出现这种问题的原因是,两个表对象都维护了关系,持久化对象可以自动更新数据库,更新客户时会修改一次外键,更新联系人时也会修改一次外键,因此产生多余的SQL语句。解决方法是使其中一方放弃维护关系即可。一般情况下,一的一方放弃维护关系。执行如下配置即可。
<set name="linkMens" inverse="true" cascade="delete,save-update" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
inverse的默认值为false,代表不放弃外键维护权,修改为true,即放弃维护。(注意, 这一点与我们的正常思维相反)
五.总结
数据库的多表设计问题是一个重要的知识点,本博客以一对多关系为例,介绍了hibernate中的多表操作。感兴趣的朋友可以
深入研究多对多的表关系,或者三表以及更多表的hibernate操作。