“Hibernate笔记 5:实体关联关系”的版本间差异
跳到导航
跳到搜索
(创建页面,内容为“category:Hibernate == 关于 == 数据库中多表之间存在着三种关联关系(一对一、一对多、多对多),用于描述实体数据之间的关系,而这种关系也可以通过对象进行描述。 实体关系 # 一对一: #: <syntaxhighlight lang="Java" highlight=""> class A{ B b; } class B{ A a; } </syntaxhighlight> #* 开发中并不常用; # '''一对多''': #: <syntaxhighlight lang="Java" highlight=""> class A{ Se…”) |
无编辑摘要 |
||
第37行: | 第37行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== 一对多 == | == 一对多 == | ||
有三种方式维护“一对多”的关联关系: | |||
1、单向关联:一对多 ——仅由“一端”维护关联关系。 | |||
2、单向关联:多对一 ——仅由“多端”维护关联关系。 | |||
3、双向关联:一对多 ——在“一端”与“多端”均维护关联关系。 | |||
'''根据不同的情况和业务需求来选择不同的关联方式。''' | |||
以“客户”(Customer)与“联系人”(LinkMan)为例: | |||
# 一个客户可以有多个联系人; | |||
# 一个联系人只能被指定给一个客户; | |||
=== 单向关联:一对多 === | |||
由“'''一端'''”维护关联关系,“多端”仅维护自身属性。 | |||
示例: | |||
* 在“Customer”中使用“linkMans”来维护其关联的“LinkMan”集合; | |||
# Customer: | # Customer: | ||
#: <syntaxhighlight lang="Java" highlight=""> | #: <syntaxhighlight lang="Java" highlight="8-9"> | ||
public class Customer { | public class Customer { | ||
private Long cust_id; | private Long cust_id; | ||
第63行: | 第73行: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
#: <syntaxhighlight lang="xml" highlight="13-17"> | |||
... | |||
<hibernate-mapping> | |||
<class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> | |||
<id name= "cust_id"> | |||
<generator class="native"/> | |||
</id> | |||
<property name="cust_name" length="32"/> | |||
<property name="cust_industry" column= "cust_industry"/> | |||
<property name="cust_level" column= "cust_level"/> | |||
<property name="cust_phone" column= "cust_phone"/> | |||
<!-- 配置关联对象集合 --> | |||
<set name="linkMans"> | |||
<key column="lkm_cust_id"></key> | |||
<one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> | |||
</set> | |||
</class> | |||
</hibernate-mapping> | |||
</syntaxhighlight> | |||
#* 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。 | |||
# LinkMan: | # LinkMan: | ||
#: <syntaxhighlight lang="Java" highlight=""> | #: <syntaxhighlight lang="Java" highlight=""> | ||
public class LinkMan { | |||
private Long lkm_id; | |||
private String 1km_name; | |||
private String lkm_phone; | |||
// getter、setter | |||
... | |||
} | |||
</syntaxhighlight> | |||
#: <syntaxhighlight lang="xml" highlight=""> | |||
... | |||
<hibernate-mapping> | |||
<class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> | |||
<id name= "lkm_id"> | |||
<generator class="native"/> | |||
</id> | |||
<property name="1km_name" length="32"/> | |||
<property name="1km_name" column= "1km_name"/> | |||
<property name="lkm_phone" column= "lkm_phone"/> | |||
</class> | |||
</hibernate-mapping> | |||
</syntaxhighlight> | |||
=== 单向关联:多对一 === | |||
“一端”仅维护自身属性,由“'''多端'''”维护关联关系。 | |||
示例: | |||
* 在“LinkMan”中使用“customer”属性来维护其关联的“Customer”; | |||
# Customer: | |||
#: <syntaxhighlight lang="Java" highlight=""> | |||
public class Customer { | |||
private Long cust_id; | |||
private String cust_name; | |||
private String cust_industry; | |||
private String cust_level; | |||
private String cust_phone; | |||
// getter、setter | |||
... | |||
} | |||
</syntaxhighlight> | |||
#: <syntaxhighlight lang="xml" highlight=""> | |||
... | |||
<hibernate-mapping> | |||
<class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> | |||
<id name= "cust_id"> | |||
<generator class="native"/> | |||
</id> | |||
<property name="cust_name" length="32"/> | |||
<property name="cust_industry" column= "cust_industry"/> | |||
<property name="cust_level" column= "cust_level"/> | |||
<property name="cust_phone" column= "cust_phone"/> | |||
</class> | |||
</hibernate-mapping> | |||
</syntaxhighlight> | |||
# LinkMan: | |||
#: <syntaxhighlight lang="Java" highlight="6-7"> | |||
public class LinkMan { | public class LinkMan { | ||
private Long lkm_id; | private Long lkm_id; | ||
第77行: | 第169行: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
#: <syntaxhighlight lang="xml" highlight="12-13"> | |||
... | |||
<hibernate-mapping> | |||
<class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> | |||
<id name= "lkm_id"> | |||
<generator class="native"/> | |||
</id> | |||
<property name="1km_name" length="32"/> | |||
<property name="1km_name" column= "1km_name"/> | |||
<property name="lkm_phone" column= "lkm_phone"/> | |||
<!-- 配置关联对象 --> | |||
<many-to-one name="customer" class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> | |||
</class> | |||
</hibernate-mapping> | |||
</syntaxhighlight> | |||
#* 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。 | |||
=== 双向关联:一对多/多对一 === | |||
在“'''一端'''”与“'''多端'''”均维护关联关系。 | |||
=== | 示例: | ||
* 在“Customer”中使用“linkMans”来维护其关联的“LinkMan”集合; | |||
* 在“LinkMan”中使用“customer”属性来维护其关联的“Customer”; | |||
#: <syntaxhighlight lang="xml" highlight=""> | # Customer: | ||
#: <syntaxhighlight lang="Java" highlight="8-9"> | |||
public class Customer { | |||
private Long cust_id; | |||
private String cust_name; | |||
private String cust_industry; | |||
private String cust_level; | |||
private String cust_phone; | |||
// 客户类中维护联系人对象集合 | |||
private Set<LinkMan> linkMans = new HashSet<LinkMan>(); | |||
// getter、setter | |||
... | |||
} | |||
</syntaxhighlight> | |||
#: <syntaxhighlight lang="xml" highlight="13-17"> | |||
... | ... | ||
<hibernate-mapping> | <hibernate-mapping> | ||
第103行: | 第232行: | ||
</hibernate-mapping> | </hibernate-mapping> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
# LinkMan. | # LinkMan: | ||
#: <syntaxhighlight lang="xml" highlight=""> | #: <syntaxhighlight lang="Java" highlight="6-7"> | ||
public class LinkMan { | |||
private Long lkm_id; | |||
private String 1km_name; | |||
private String lkm_phone; | |||
// 联系人类中维护客户对象 | |||
private Customer customer; | |||
// getter、setter | |||
... | |||
} | |||
</syntaxhighlight> | |||
#: <syntaxhighlight lang="xml" highlight="12-13"> | |||
... | ... | ||
<hibernate-mapping> | <hibernate-mapping> | ||
第121行: | 第263行: | ||
</hibernate-mapping> | </hibernate-mapping> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* | * 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。 | ||
== 多对多 == | |||
== cascade(级联操作) == | |||
“级联操作”:是指当“'''主控方'''”执行“保存”、“更新”或者“删除”操作时,其关联对象(“被控方”)也执行相同的操作。 | |||
1、在映射文件中通过对 cascade 属性的设置来控制是否对关联对象采用级联操作。 | |||
2、级联操作对各种关联关系都是有效的。 | |||
以下,以“一对多”关系为例。 | |||
'''级联是有方向性的''':通过“一端级联操作多端”或“多端级联操作一端”来进行级联操作,二者的含义是完全不同的。 | |||
=== 级联更新 === | |||
==== 主控端:“一端” ==== | |||
主控端为“一端”,即“'''单向关联:一对多'''”。 | |||
Customer: | |||
<syntaxhighlight lang="xml" highlight="3"> | |||
... | |||
<!-- 配置关联对象集合 --> | |||
<set name="linkMans" cascade=""save-update> | |||
<key column="lkm_cust_id"></key> | |||
<one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> | |||
</set> | |||
... | |||
</syntaxhighlight> | |||
测试代码: | |||
: <syntaxhighlight lang="Java" highlight="18-23"> | |||
@Test | |||
// 保存客户,并级联保存其联系人 | |||
public void demo1() { | |||
Session session = HibernateUtils.openSession(); | |||
Transaction tx = session.beginTransaction(); | |||
// 创建客户 | |||
Customer customer = new Customer(); | |||
customer.setCust_name("中国移动"); | |||
// 创建联系人 | |||
LinkMan linkManl = new LinkMan(); | |||
linkManl.setLkm_name("老张"); | |||
LinkMan linkMan2 = new LinkMan(); | |||
linkMan2.setLkm_name("老李"); | |||
// 建立关联关系 | |||
customer.getLinkMans().add(linkManl); | |||
customer.getLinkMans().add(linkMan2); | |||
// 保存 | |||
session.save(customer); | |||
tx.commit(); | |||
} | |||
</syntaxhighlight> | |||
==== 主控端:“多端” ==== | |||
主控端为“多端”,即“'''单向关联:多对一'''”。 | |||
LinkMan: | |||
<syntaxhighlight lang="xml" highlight="3"> | |||
... | |||
<!-- 配置关联对象 --> | |||
<many-to-one name="customer" cascade=""save-update class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> | |||
... | |||
</syntaxhighlight> | |||
测试代码: | 测试代码: | ||
: <syntaxhighlight lang="Java" highlight="18-24"> | |||
@Test | |||
// 保存联系人,并级联保存其客户 | |||
public void demo2() { | |||
Session session = HibernateUtils.openSession(); | |||
Transaction tx = session.beginTransaction(); | |||
// 创建客户 | |||
Customer customer = new Customer(); | |||
customer.setCust_name("中国移动"); | |||
// 创建联系人 | |||
LinkMan linkManl = new LinkMan(); | |||
linkManl.setLkm_name("老张"); | |||
LinkMan linkMan2 = new LinkMan(); | |||
linkMan2.setLkm_name("老李"); | |||
// 建立关联关系 | |||
linkManl.setCustomer(customer); | |||
linkMan2.setCustomer(customer); | |||
// 保存 | |||
session.save(linkManl); | |||
session.save(linkManl); | |||
tx.commit(); | |||
} | |||
</syntaxhighlight> | |||
=== 级联删除 === | |||
==== 主控端:“一端” ==== | |||
==== 主控端:“多端” ==== | |||
=== 级联操作与 SQL === | |||
== inverse(关系维护) == | |||
== FAQ == | |||
=== 不配置 cascade 是否可以进行“更新”? === | |||
不配置 cascade 则不可以级联更新。否则,可能出现“瞬时对象异常”(org.hibemate.TransientObjectException)。 | |||
示例: | |||
可以通过“'''双向关联'''”的方式完成更新,但是:代码更多,执行的 SQL 也更多。 | |||
示例:(非级联更新,必须为:“双向关联:一对多/多对一”) | |||
: <syntaxhighlight lang="Java" highlight=""> | : <syntaxhighlight lang="Java" highlight=""> | ||
@Test | @Test | ||
// | // 非级联保存:一个客户,及其两个联系人 | ||
public void | public void demo() { | ||
Session session = HibernateUtils.openSession(); | Session session = HibernateUtils.openSession(); | ||
Transaction tx = session.beginTransaction(); | Transaction tx = session.beginTransaction(); | ||
第158行: | 第430行: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
如上代码: | |||
# 建立关联时:Customer 对象关联了 LinkMan 对象,同时 LinkMan 对象也关联了 Customer 对象; | # 建立关联时:Customer 对象关联了 LinkMan 对象,同时 LinkMan 对象也关联了 Customer 对象; | ||
# 保存对象时:对 Customer 对象进行了保存,也分别对 LinkMan 对象进行了保存; | # 保存对象时:对 Customer 对象进行了保存,也分别对 LinkMan 对象进行了保存; | ||
从控制台打印出的 SQL 语句可以看出: | #* 从控制台打印出的 SQL 语句可以看出: | ||
# 执行了三次 insert(对 customer、linkManl、linkMan2) | #*# 执行了三次 insert(对 customer、linkManl、linkMan2) | ||
# 执行了两次 update(对 linkManl、linkMan2 更新外键) | #*# 执行了两次 update(对 linkManl、linkMan2 更新外键) | ||
: <syntaxhighlight lang="Java" highlight=""> | #*: <syntaxhighlight lang="Java" highlight=""> | ||
Hibernate: | Hibernate: | ||
insert | insert | ||
第202行: | 第474行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== 不配置 cascade 是否可以进行“删除”? === | |||
=== | |||
2022年6月20日 (一) 23:56的版本
关于
数据库中多表之间存在着三种关联关系(一对一、一对多、多对多),用于描述实体数据之间的关系,而这种关系也可以通过对象进行描述。
实体关系
- 一对一:
class A{ B b; } class B{ A a; }
- 开发中并不常用;
- 一对多:
class A{ Set<B> bs; } class B{ A a; }
- 多对多:
class A{ Set<B> bs; } class B{ Set<A> as; }
一对多
有三种方式维护“一对多”的关联关系: 1、单向关联:一对多 ——仅由“一端”维护关联关系。 2、单向关联:多对一 ——仅由“多端”维护关联关系。 3、双向关联:一对多 ——在“一端”与“多端”均维护关联关系。 根据不同的情况和业务需求来选择不同的关联方式。
以“客户”(Customer)与“联系人”(LinkMan)为例:
- 一个客户可以有多个联系人;
- 一个联系人只能被指定给一个客户;
单向关联:一对多
由“一端”维护关联关系,“多端”仅维护自身属性。
示例:
- 在“Customer”中使用“linkMans”来维护其关联的“LinkMan”集合;
- Customer:
public class Customer { private Long cust_id; private String cust_name; private String cust_industry; private String cust_level; private String cust_phone; // 客户类中维护联系人对象集合 private Set<LinkMan> linkMans = new HashSet<LinkMan>(); // getter、setter ... }
... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> <id name= "cust_id"> <generator class="native"/> </id> <property name="cust_name" length="32"/> <property name="cust_industry" column= "cust_industry"/> <property name="cust_level" column= "cust_level"/> <property name="cust_phone" column= "cust_phone"/> <!-- 配置关联对象集合 --> <set name="linkMans"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> </class> </hibernate-mapping>
- 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。
- LinkMan:
public class LinkMan { private Long lkm_id; private String 1km_name; private String lkm_phone; // getter、setter ... }
... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> <id name= "lkm_id"> <generator class="native"/> </id> <property name="1km_name" length="32"/> <property name="1km_name" column= "1km_name"/> <property name="lkm_phone" column= "lkm_phone"/> </class> </hibernate-mapping>
单向关联:多对一
“一端”仅维护自身属性,由“多端”维护关联关系。
示例:
- 在“LinkMan”中使用“customer”属性来维护其关联的“Customer”;
- Customer:
public class Customer { private Long cust_id; private String cust_name; private String cust_industry; private String cust_level; private String cust_phone; // getter、setter ... }
... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> <id name= "cust_id"> <generator class="native"/> </id> <property name="cust_name" length="32"/> <property name="cust_industry" column= "cust_industry"/> <property name="cust_level" column= "cust_level"/> <property name="cust_phone" column= "cust_phone"/> </class> </hibernate-mapping>
- LinkMan:
public class LinkMan { private Long lkm_id; private String 1km_name; private String lkm_phone; // 联系人类中维护客户对象 private Customer customer; // getter、setter ... }
... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> <id name= "lkm_id"> <generator class="native"/> </id> <property name="1km_name" length="32"/> <property name="1km_name" column= "1km_name"/> <property name="lkm_phone" column= "lkm_phone"/> <!-- 配置关联对象 --> <many-to-one name="customer" class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> </class> </hibernate-mapping>
- 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。
双向关联:一对多/多对一
在“一端”与“多端”均维护关联关系。
示例:
- 在“Customer”中使用“linkMans”来维护其关联的“LinkMan”集合;
- 在“LinkMan”中使用“customer”属性来维护其关联的“Customer”;
- Customer:
public class Customer { private Long cust_id; private String cust_name; private String cust_industry; private String cust_level; private String cust_phone; // 客户类中维护联系人对象集合 private Set<LinkMan> linkMans = new HashSet<LinkMan>(); // getter、setter ... }
... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.Customer" table="cst_customer"> <id name= "cust_id"> <generator class="native"/> </id> <property name="cust_name" length="32"/> <property name="cust_industry" column= "cust_industry"/> <property name="cust_level" column= "cust_level"/> <property name="cust_phone" column= "cust_phone"/> <!-- 配置关联对象集合 --> <set name="linkMans"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> </class> </hibernate-mapping>
- LinkMan:
public class LinkMan { private Long lkm_id; private String 1km_name; private String lkm_phone; // 联系人类中维护客户对象 private Customer customer; // getter、setter ... }
... <hibernate-mapping> <class name="cn.itcast.hibernate.domain.LinkMan" table="cst_linkMan"> <id name= "lkm_id"> <generator class="native"/> </id> <property name="1km_name" length="32"/> <property name="1km_name" column= "1km_name"/> <property name="lkm_phone" column= "lkm_phone"/> <!-- 配置关联对象 --> <many-to-one name="customer" class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> </class> </hibernate-mapping>
- 注意:“lkm_cust_id”为“cst_linkMan”表的外键(参照于“cst_customer”表)。
多对多
cascade(级联操作)
“级联操作”:是指当“主控方”执行“保存”、“更新”或者“删除”操作时,其关联对象(“被控方”)也执行相同的操作。 1、在映射文件中通过对 cascade 属性的设置来控制是否对关联对象采用级联操作。 2、级联操作对各种关联关系都是有效的。
以下,以“一对多”关系为例。
级联是有方向性的:通过“一端级联操作多端”或“多端级联操作一端”来进行级联操作,二者的含义是完全不同的。
级联更新
主控端:“一端”
主控端为“一端”,即“单向关联:一对多”。
Customer:
...
<!-- 配置关联对象集合 -->
<set name="linkMans" cascade=""save-update>
<key column="lkm_cust_id"></key>
<one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/>
</set>
...
测试代码:
@Test // 保存客户,并级联保存其联系人 public void demo1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkManl = new LinkMan(); linkManl.setLkm_name("老张"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("老李"); // 建立关联关系 customer.getLinkMans().add(linkManl); customer.getLinkMans().add(linkMan2); // 保存 session.save(customer); tx.commit(); }
主控端:“多端”
主控端为“多端”,即“单向关联:多对一”。
LinkMan:
...
<!-- 配置关联对象 -->
<many-to-one name="customer" cascade=""save-update class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/>
...
测试代码:
@Test // 保存联系人,并级联保存其客户 public void demo2() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkManl = new LinkMan(); linkManl.setLkm_name("老张"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("老李"); // 建立关联关系 linkManl.setCustomer(customer); linkMan2.setCustomer(customer); // 保存 session.save(linkManl); session.save(linkManl); tx.commit(); }
级联删除
主控端:“一端”
主控端:“多端”
级联操作与 SQL
inverse(关系维护)
FAQ
不配置 cascade 是否可以进行“更新”?
不配置 cascade 则不可以级联更新。否则,可能出现“瞬时对象异常”(org.hibemate.TransientObjectException)。
示例:
可以通过“双向关联”的方式完成更新,但是:代码更多,执行的 SQL 也更多。
示例:(非级联更新,必须为:“双向关联:一对多/多对一”)
@Test // 非级联保存:一个客户,及其两个联系人 public void demo() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkManl = new LinkMan(); linkManl.setLkm_name("老张"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("老李"); // 建立关联关系 customer.getLinkMans().add(linkManl); customer.getLinkMans().add(linkMan2); linkManl.setCustomer(customer); linkMan2.setCustomer(customer); // 保存 session.save(customer); session.save(linkManl); session.save(linkManl); tx.commit(); }
如上代码:
- 建立关联时:Customer 对象关联了 LinkMan 对象,同时 LinkMan 对象也关联了 Customer 对象;
- 保存对象时:对 Customer 对象进行了保存,也分别对 LinkMan 对象进行了保存;
- 从控制台打印出的 SQL 语句可以看出:
- 执行了三次 insert(对 customer、linkManl、linkMan2)
- 执行了两次 update(对 linkManl、linkMan2 更新外键)
Hibernate: insert into cst_customer (cust_name, cust_industry, cust_level, cust_phone) values (?, ?, ?, ?) Hibernate: insert into cst_linkMan (1km_name, lkm_phone, lkm_cust_id) values (?, ?, ?, ?) Hibernate: insert into cst_linkMan (1km_name, lkm_phone, lkm_cust_id) values (?, ?, ?, ?) Hibernate: update cst_linkMan set lkm_cust_id where lkm_id=? Hibernate: update cst_linkMan set lkm_cust_id where lkm_id=?
- 从控制台打印出的 SQL 语句可以看出: