表關(guān)系的分析
數(shù)據(jù)庫中多表之間存在著三種關(guān)系:

表與表的三種關(guān)系:
一對(duì)多|多對(duì)一
建表原則:在多的一方創(chuàng)建外鍵指向一的一方的主鍵

表中的表達(dá)

實(shí)體中的表達(dá)

orm元數(shù)據(jù)表達(dá)
- 多對(duì)一配置
<!--表示多對(duì)一-->
<many-to-one name="customer"
column="lkm_cust_id"
class="Customer">
</many-to-one>
這里的這個(gè)配置表示的是:當(dāng)前的這個(gè)配置對(duì)象存在多對(duì)一的情況,其中name表示實(shí)體中多對(duì)一的一的名字,column表示外鍵的列名 class表示的是多個(gè)對(duì)一個(gè)的一的實(shí)體類名
- 一對(duì)多配置
<!--配置集合 一對(duì)多的關(guān)系-->
<set name="linkMens">
<!--指定外鍵列名-->
<key column="lkm_cust_id"></key>
<!--指定一對(duì)多還是多對(duì)多
class指定是與哪個(gè)類型一對(duì)多
-->
<one-to-many class="LinkMan"/>
</set>
這里的這個(gè)配置表示的是:當(dāng)前的這個(gè)配置對(duì)象存在多對(duì)一的情況,其中name表示實(shí)體中多個(gè)的set集合,key column表示外鍵的列名 class表示的是一對(duì)多的多的實(shí)體類名
/**
*保存客戶以及客戶下的聯(lián)系人
*/
@Test
public void fun1() {
//獲得session
Session session = HibernateUtils.openSession();
//開啟事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作數(shù)據(jù)
Customer customer = new Customer();
customer.setCust_name("xxx");
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("aaa1");
LinkMan lm2 = new LinkMan();
lm2.setLkm_name("aaa2");
//表達(dá)一對(duì)多關(guān)系 客戶下有多個(gè)聯(lián)系人
customer.getLinkMens().add(lm1);
customer.getLinkMens().add(lm2);
//表達(dá)多對(duì)一 聯(lián)系人屬于哪個(gè)客戶
lm1.setCustomer(customer);
lm2.setCustomer(customer);
//瞬時(shí)狀態(tài)對(duì)象---->持久化狀態(tài)
session.save(customer);
session.save(lm1);
session.save(lm2);
//----------------------------
//提交事物
tx.commit();
//關(guān)閉資源
session.close();
}
/**
* 為客戶添加聯(lián)系人
*/
@Test
public void fun2() {
//獲得session
Session session = HibernateUtils.openSession();
//開啟事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作數(shù)據(jù)
//獲得要操作的客戶對(duì)象
Customer customer = session.get(Customer.class, 1l);
//創(chuàng)建聯(lián)系人
LinkMan lm1 = new LinkMan();
//將聯(lián)系人添加到客戶,將客戶設(shè)置到聯(lián)系人中
customer.getLinkMens().add(lm1);
lm1.setCustomer(customer);
//執(zhí)行保存
session.save(customer);
session.save(lm1);
//----------------------------
//提交事物
tx.commit();
//關(guān)閉資源
session.close();
}
/**
* 從客戶中刪除某個(gè)聯(lián)系人
*/
@Test
public void fun3() {
//獲得session
Session session = HibernateUtils.openSession();
//開啟事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作數(shù)據(jù)
//獲得客戶
Customer c = session.get(Customer.class, 1l);
LinkMan lm = session.get(LinkMan.class, 3l);
//獲得客戶的聯(lián)系人
//將要移除的聯(lián)系人從客戶的聯(lián)系人集合中移除
c.getLinkMens().remove(lm);
//清空聯(lián)系人的客戶
lm.setCustomer(null);
//----------------------------
//提交事物
tx.commit();
//關(guān)閉資源
session.close();
}
上面的代碼再刪除某個(gè)聯(lián)系人時(shí)由于數(shù)據(jù)都是查詢出來的,都是出于持久態(tài)的對(duì)象,所以再事務(wù)進(jìn)行提交時(shí)Hibernate會(huì)查看數(shù)據(jù)是否發(fā)生變化,發(fā)生變化后會(huì)將數(shù)據(jù)更新,所以不需要執(zhí)行session.save()方法進(jìn)行保存數(shù)據(jù)
操作進(jìn)階
在上面的操作中,我們?cè)诓僮鞅4婵蛻舻穆?lián)系人的時(shí)候,我們需要執(zhí)行多條保存的代碼,這樣顯然很麻煩,我們希望再保存客戶的時(shí)候,將客戶的聯(lián)系人也一起保存。所以我們需要進(jìn)行一些別的配置
- 級(jí)聯(lián)操作
<!--級(jí)聯(lián)操作:cascade
save-update:級(jí)聯(lián)保存更新
delete:級(jí)聯(lián)刪除
all: save-update delete 級(jí)聯(lián)保存更新和級(jí)聯(lián)刪除
級(jí)聯(lián)操作:簡化操作
-->
<set name="linkMens" cascade="all">
<!--配置集合 一對(duì)多的關(guān)系-->
<!--指定外鍵列名-->
<key column="lkm_cust_id"></key>
<!--指定一對(duì)多還是多對(duì)多
class指定是與哪個(gè)類型一對(duì)多
-->
<one-to-many class="LinkMan"/>
</set>
級(jí)聯(lián)操作應(yīng)用在一對(duì)多的多的時(shí)候,也可以實(shí)現(xiàn)保存聯(lián)系人的時(shí)候?qū)⒖蛻舯4?/p>
- linkman.hbm.xml 配置級(jí)聯(lián)操作
<!--表示多對(duì)一-->
<many-to-one name="customer"
column="lkm_cust_id"
class="Customer"
cascade="all"
>
操作代碼
/**
* 級(jí)聯(lián)操作 保存聯(lián)系人 級(jí)聯(lián)保存客戶
*/
@Test
public void fun5() {
//獲得session
Session session = HibernateUtils.openSession();
//開啟事物
Transaction tx = session.beginTransaction();
//----------------------------
//操作數(shù)據(jù)
//創(chuàng)建客戶
Customer customer = new Customer();
customer.setCust_name("google company");
//創(chuàng)建聯(lián)系人
LinkMan lm = new LinkMan();
lm.setLkm_name("劈柴哥");
//為客戶添加聯(lián)系人
customer.getLinkMens().add(lm);
//為聯(lián)系人指定客戶
lm.setCustomer(customer);
//保存客戶----> 只保存聯(lián)系人 通過級(jí)聯(lián)操作保存客戶
session.saveOrUpdate(lm);
//----------------------------
//提交事物
tx.commit();
//關(guān)閉資源
session.close();
}
在平時(shí)開發(fā)中,建議使用save-update 不建議使用delete 級(jí)聯(lián)刪除會(huì)刪除相關(guān)的所有數(shù)據(jù)
- 關(guān)系維護(hù)
我們查看操作時(shí)Hibernate產(chǎn)生的SQL語句時(shí)發(fā)現(xiàn)了如下的語句
Hibernate執(zhí)行的語句.png
我們發(fā)現(xiàn)。在保存時(shí),客戶和聯(lián)系人兩房都會(huì)維護(hù)外鍵的關(guān)系,關(guān)系會(huì)維護(hù)兩次,這樣冗余了。多余的維護(hù)關(guān)系語句。顯然是客戶這一端在維護(hù)關(guān)系。所以我們需要優(yōu)化這種情況 這就引入了Hibernate在實(shí)體類配置的時(shí)候提供的配置 inverse
inverse是為了提高效率
<!--
inverse屬性:配置關(guān)系是否維護(hù)
配置當(dāng)前的Customer是否維護(hù)與LinkMan的關(guān)系
inverse true 的意思就是將關(guān)系維護(hù)的工作完全交給對(duì)方 作為配置方不進(jìn)行維護(hù)關(guān)系
* 不維護(hù)關(guān)系無法在刪除的時(shí)候?qū)⑼怄I置為空
false(默認(rèn)值) 意思是將關(guān)系維護(hù)的工作由配置方來維護(hù)
原則:無論關(guān)系方如何放棄關(guān)系,總有一方必須要維護(hù)關(guān)系
一對(duì)多關(guān)系當(dāng)中,也只能是一的一方放棄維護(hù),多的一方不能放棄
-->
<set name="linkMens" inverse="true">
<key column="lkm_cust_id"></key>
class指定是與哪個(gè)類型一對(duì)多
<one-to-many class="LinkMan"/>
</set>
tips:在開發(fā)中的刪除數(shù)據(jù),結(jié)合inverse 和cascade 實(shí)現(xiàn)不同的數(shù)據(jù)刪除策略。以這個(gè)demo為例,一個(gè)客戶可以由多個(gè)聯(lián)系人。在這種情況下 相當(dāng)于客戶和聯(lián)系人維持著某種關(guān)系。我們?cè)谟行枨髣h除客戶的時(shí)候?qū)β?lián)系人的刪除有兩種。
第一種:刪除Customer的時(shí)候,將LinkMan的外鍵置為空,LinkMan數(shù)據(jù)保留
這種情況下,如果Customer不維護(hù)數(shù)據(jù)的話是無法操作LinkMan的外鍵的,所以在這種情況下我們需要讓Customer inverse配置為false 讓customer維護(hù)關(guān)系,在這種情況下刪除Customer的時(shí)候會(huì)刪除LinkMan的關(guān)系。
第二種:刪除Customer的時(shí)候,級(jí)聯(lián)操作將LinkMan的數(shù)據(jù)刪除。
在這種情況下,將Customer inverse設(shè)置為true 并且將cascade指定為delete 這樣在刪除Customer的同時(shí)就會(huì)刪除LinkMan子表中的數(shù)據(jù)。
多對(duì)多關(guān)聯(lián)關(guān)系映射操作
建表原則:創(chuàng)建一個(gè)中間表,中間表中至少兩個(gè)字段作為外鍵分別指向多對(duì)多雙方的主鍵

表中關(guān)系表達(dá)

實(shí)體中的表達(dá)

orm元數(shù)據(jù)表達(dá)
- User表配置
<set name="roles" table="sys_user_role">
<!--
key標(biāo)簽
column:當(dāng)前對(duì)象在中間表中的外鍵名稱
-->
<key column="user_id"></key>
<!--
many-to-many標(biāo)簽:
class:關(guān)聯(lián)另一方的類的全類名
column 關(guān)聯(lián)的另一方在中間表的外鍵名稱
-->
<many-to-many class="Role" column="role_id"></many-to-many>
</set>
- role表配置
<!--Role多對(duì)多配置-->
<set name="users" table="sys_user_role">
<key column="role_id"/>
<many-to-many class="User"
column="user_id"/>
</set>
多對(duì)多操作
我們使用案例:保存員工以及角色
在多對(duì)多關(guān)系中一定要有一方放棄關(guān)系的維護(hù),否則會(huì)出現(xiàn)主鍵重復(fù)錯(cuò)誤
放棄關(guān)系維護(hù)在配置文件中使用 inverse=“true” 表示放棄操作
- 配置文件
<!-- inverse 屬性 使得一方放棄維護(hù)外鍵關(guān)系-->
<set name="users" table="sys_user_role" inverse="true">
<key column="role_id"/>
<many-to-many class="User"
column="user_id"/>
</set>
- 實(shí)例代碼
//從工廠獲得session
Session session = HibernateUtil.openSession();
//開啟事物
Transaction tx = session.beginTransaction();
//操作數(shù)據(jù)
//-------------------------------
//創(chuàng)建兩個(gè)User
User user1 = new User();
User user2 = new User();
user1.setUser_name("blackman");
user2.setUser_name("whiteman");
//創(chuàng)建兩個(gè)role
Role role1 = new Role();
Role role2 = new Role();
Role role3 = new Role();
role1.setRole_name("vp");
role2.setRole_name("cto");
role3.setRole_name("security");
//用戶表達(dá)關(guān)系
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role1);
user2.getRoles().add(role3);
//角色表達(dá)關(guān)系
role1.getUsers().add(user1);
role1.getUsers().add(user2);
role2.getUsers().add(user1);
role3.getUsers().add(user2);
//保存到數(shù)據(jù)庫
session.save(user1);
session.save(user2);
session.save(role1);
session.save(role2);
session.save(role3);
//-------------------------------
//提交事務(wù)
tx.commit();
//關(guān)閉資源
session.close();
@Test
/**
* 添加角色到某個(gè)用戶中
*/
public void addRoleToUser() {
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
//操作
//------------
//獲得要添加的用戶
User user = session.get(User.class, 1l);
//創(chuàng)建公關(guān)角色
Role role = new Role();
role.setRole_name("公關(guān)");
//將角色添加到用戶
user.getRoles().add(role);
//將角色轉(zhuǎn)換為持久化
session.save(role);
//------------
tx.commit();
session.close();
}
/**
* 從指定角色刪除
*/
@Test
public void delRoleFromUser(){
//獲取session
Session session = HibernateUtil.openSession();
//開啟事物
Transaction tx = session.beginTransaction();
//操作數(shù)據(jù)
//-------------
//獲得要?jiǎng)h除的數(shù)據(jù)
User user = session.get(User.class, 3l);
//獲得要?jiǎng)h除的角色數(shù)據(jù)
Role role = session.get(Role.class, 7l);
user.getRoles().remove(role);
//-------------
//提交事務(wù)
tx.commit();
//關(guān)閉資源
session.close();
}
進(jìn)階操作
- 級(jí)聯(lián)操作
cascade: 級(jí)聯(lián)操作
在User.hbm.xml中配置cascade后
<set name="roles" table="sys_user_role" cascade="save-update">
可以進(jìn)行級(jí)聯(lián)保存 上面的操作代碼不再需要
session.save(role)
一對(duì)一
- 建表原則:
- 1 唯一外鍵對(duì)應(yīng):假設(shè)一對(duì)一種的任意一方為多,在多的一方創(chuàng)建外鍵指向一的一方的主鍵,然后將外鍵設(shè)置為唯一。
-
2 主鍵對(duì)應(yīng):一方的主鍵作為另一方的主鍵
一對(duì)一

