Hibernate笔记 5:实体关联关系

来自Wikioe
跳到导航 跳到搜索


关于

数据库中多表之间存在着三种关联关系(一对一、一对多、多对多),用于描述实体数据之间的关系,而这种关系也可以通过对象进行描述。

见:

  1. Hibernate笔记 5:实体关联关系:一对一
  2. Hibernate笔记 5:实体关联关系:一对多
  3. Hibernate笔记 5:实体关联关系:多对多

cascade(级联操作)

“级联操作”:是指当“主控方”执行“保存”、“更新”或者“删除”操作时,其关联对象(“被控方”)也执行相同的操作。

1、在映射文件中通过对 cascade 属性的设置来控制是否对关联对象采用级联操作。
2、级联操作对各种关联关系都是有效的。

级联属性

cascade 属性的可选值:

  1. all:所有情况下均进行关联操作。【即 save-update + delete
  2. none:所有情况下均不进行关联操作。【默认值】
  3. save-update:在执行 save / update / saveOrUpdate 时进行关联操作。
  4. delete:在执行 delete 时进行关联操作。
  5. 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();
	}

如上代码:

  1. “session.save(linkManl)”:将会产生 4 条 insert 语句;
    因为:linkManl 关联了 customer;而 customer 又关联了 linkMan2、linkMan3;
  2. “session.save(customer)”:将会产生 3 条 insert 语句;
    因为:customer 关联了 linkMan2、linkMan3,但未关联 linkManl;
    (“linkManl 关联了 customer” ≠ “customer 关联了 linkManl”)
  3. “session.save(linkMan2)”:将会产生 1 条 insert 语句;
    因为:linkMan2 未关联其他对象;
    (“customer 关联了 linkMan2” ≠ “linkMan2 关联了 customer”)


P.S.  “a 关联了 b”    “b 关联了 a”

inverse(关系维护)

inverse 属性用于设置“是否由对方维护关联关系”。

可选值:true / false(默认)

inverse 作用:

  1. 级联维护:配置为 true 的一方所设置的级联将会失效。
    • inverse 的权限在 cascade 之上。
  2. 外键维护:配置为 true 的一方将不再维护关联的外键字段。
    • 维护外键的一方必须拥有对方的实例引用。

外键维护

通过如下示例,了解 inverse 对外键维护的影响。

“双向关联:一对多”,有配置如下:

  1. 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>
    		...
    
  2. 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