Hibernate笔记 5:实体关联关系
跳到导航
跳到搜索
关于
数据库中多表之间存在着三种关联关系(一对一、一对多、多对多),用于描述实体数据之间的关系,而这种关系也可以通过对象进行描述。
见:
cascade(级联操作)
“级联操作”:是指当“主控方”执行“保存”、“更新”或者“删除”操作时,其关联对象(“被控方”)也执行相同的操作。 1、在映射文件中通过对 cascade 属性的设置来控制是否对关联对象采用级联操作。 2、级联操作对各种关联关系都是有效的。
级联属性
cascade 属性的可选值:
- all:所有情况下均进行关联操作。【即 save-update + delete】
- none:所有情况下均不进行关联操作。【默认值】
- save-update:在执行 save / update / saveOrUpdate 时进行关联操作。
- delete:在执行 delete 时进行关联操作。
- all-delete-orphan:当一个节点在对象图中成为孤儿节点时,删除该节点。
级联的方向性
级联是有方向性的:
以“一对多”关系为例,“主控方为一端”(通过一端级联操作多端)和“主控方为多端”(通过多端级联操作一端),二者的含义是完全不同的。
示例:
- “双向关联:一对多/多对一”;
- Customer.hbm.xml、LinkMan.hbm.xml 均配置了级联更新“cascade="save-update"”;
@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("老李"); LinkMan linkMan3 = new LinkMan(); linkMan3.setLkm_name("老王"); //建立关系: linkManl.setCustomer(customer); customer.getLinkMans().add(linkMan2); customer.getLinkMans().add(linkMan3); // 保存 //session.save(linkManl); // 发送几条 insert 语句? //session.save(customer); // 发送几条 insert 语句? //session.save(linkMan2); // 发送几条 insert 语句? tx.commit(); }
如上代码:
- “session.save(linkManl)”:将会产生 4 条 insert 语句;
- 因为:linkManl 关联了 customer;而 customer 又关联了 linkMan2、linkMan3;
- “session.save(customer)”:将会产生 3 条 insert 语句;
- 因为:customer 关联了 linkMan2、linkMan3,但未关联 linkManl;
- (“linkManl 关联了 customer” ≠ “customer 关联了 linkManl”)
- “session.save(linkMan2)”:将会产生 1 条 insert 语句;
- 因为:linkMan2 未关联其他对象;
- (“customer 关联了 linkMan2” ≠ “linkMan2 关联了 customer”)
P.S. “a 关联了 b” ≠ “b 关联了 a”
inverse(关系维护)
inverse 属性用于设置“是否由对方维护关联关系”。 可选值:true / false(默认)
inverse 作用:
- 级联维护:配置为 true 的一方所设置的级联将会失效。
- inverse 的权限在 cascade 之上。
- 外键维护:配置为 true 的一方将不再维护关联的外键字段。
- 维护外键的一方必须拥有对方的实例引用。
外键维护
通过如下示例,了解 inverse 对外键维护的影响。
“双向关联:一对多”,有配置如下:
- Customer.hbm.xml:
... <set name="linkMans" cascade="save-update"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> ...
- LinkMan.hbm.xml:
... <many-to-one name="customer" cascade="save-update" class="cn.itcast.hibernate.domain.Customer" column="lkm_cust_id"/> ...
示例1:未配置 inverse;通过 LinkMan 级联保存。
@Test // 保存联系人,并级联保存其客户 public void demo1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("老张"); // 建立关联关系 linkMan.setCustomer(customer); // 保存 session.save(linkMan); tx.commit(); }
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: update cst_linkMan set lkm_cust_id where lkm_id=?
如上,Hibernate 后台执行了一条 update 语句:在级联保存 customer 时将维护 cst_linkMan 表的 lkm_cust_id。
- 由于主控端为“多端”,所以“多端”的 insert 中外键字段不空。
- 由于 Customer 同时维护外键,所以此处 update 语句是多余的;
示例2:Customer 端配置 inverse;通过 LinkMan 级联保存。
... <set name="linkMans" cascade="save-update" inverse="true"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> ...
- 同样对于上述代码,后台 sql 如下:
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 后台将并不执行任何 update 语句。
- 由于主控端为“多端”,所以“多端”的 insert 中外键字段不空。
- 由于 Customer 放弃维护外键,所以没有 update 语句;
示例3:Customer 端配置 inverse;通过 Customer 级联保存。
... <set name="linkMans" cascade="save-update" inverse="true"> <key column="lkm_cust_id"></key> <one-to-many class= "cn.itcast.hibernate.domain.LinkMan"/> </set> ...
@Test // 保存客户,并级联保存其联系人 public void demo2() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); // 创建客户 Customer customer = new Customer(); customer.setCust_name("中国移动"); // 创建联系人 LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("老张"); // 建立关联关系 customer.getLinkMans().add(linkMan); // 保存 session.save(customer); tx.commit(); }
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 后台将并不执行任何 update 语句,但数据库 cst_linkMan 表的外键 lkm_cust_id 为 null。
- 由于主控端为“一端”,所以“多端”的 insert 中外键字段为空。
- 由于 Customer 放弃维护外键,所以没有 update 语句;
- 最终导致外键字段为 null。
由以上可知,对于“一对多”关系,通常由“多端”作为主控端,并维护外键(inverse 配置在“一端”)。 P.S. “一对多”关系级联更新(保存)时: 1、主控端为“一端”,sql 执行顺序: insert + insert + update 2、主控端为“多端”,sql 执行顺序: insert + insert