課程回顧:Hibernate第二天
1. 持久化類和一級緩存
* 持久化類:JavaBean + 映射的配置文件
* 持久化對象的三種狀態
* 瞬時態
* 持久態:有自動更新數據的能力
* 託管態
* Session的一級緩存,快照機制
* 主鍵的生成策略
2. 管理事務
* 設置隔離級別
* 丟失更新的問題,樂觀鎖:添加屬性version,配置<version name="version">
* 綁定本地的Session,事務需要service層開啓,dao層需要使用Session對象
3. 查詢的接口
* Query接口:HQL的查詢
* Criteria接口:QBC查詢(按條件進行查詢)
一對多(添加聯繫人案例)
因爲客戶和聯繫人是一對多的關係,在有客戶的情況下,完成聯繫人的添加保存操作
javaWEB中一對多的設計以及建表原則
自己滾去複習
導入SQL的建表語句
編寫JavaBean(注意一對多的編寫規則)
客戶的JavaBean(一方)
public class Customer {
private Long cust_id;
private String cust_name;
private Long cust_user_id;
private Long cust_create_id;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
//一對多 一方寫個集合
//hibernate框架默認的集合是 set
//必須自己手動初始化
private Set<Linkman> linkmans = new HashSet<Linkman>();
}
聯繫人的JavaBean(多方)
public class Linkman {
private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
//老生常談的問題 外鍵直接寫對象
private Customer customer;
}
編寫客戶和聯繫人的映射配置文件(注意一對多的配置編寫)
客戶的配置文件(一方)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--配置類跟表結構的映射-->
<class name="com.xiaoming.day01.domain.Customer" table="cst_customer">
<id name="cust_id" column="cust_id">
<!--主鍵生成策略-->
<generator class="native"/>
</id>
<!--配置樂觀鎖-->
<version name="version"/>
<!-- 配置其他的屬性 -->
<property name="cust_name" column="cust_name"/>
<property name="cust_user_id" column="cust_user_id"/>
<property name="cust_create_id" column="cust_create_id"/>
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_linkman" column="cust_linkman"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
<!--
配置一方:
set標籤的 name屬性 :表示集合的名稱
-->
<set name="linkmans">
<!--
需要出現的子標籤
key: 多的一方的外鍵名稱
-->
<key column="lkm_cust_id"></key>
<one-to-many class="com.xiaoming.day01.domain.Linkman"/>
</set>
</class>
</hibernate-mapping>
聯繫人的配置文件(多方)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--配置類跟表結構的映射-->
<class name="com.xiaoming.day01.domain.Linkman" table="cst_linkman">
<id name="lkm_id" column="lkm_id">
<!--主鍵生成策略-->
<generator class="native"/>
</id>
<!--配置樂觀鎖-->
<version name="version"/>
<!-- 配置其他的屬性 -->
<property name="lkm_name" column="lkm_name"/>
<property name="lkm_gender" column="lkm_gender"/>
<property name="lkm_phone" column="lkm_phone"/>
<property name="lkm_mobile" column="lkm_mobile"/>
<property name="lkm_email" column="lkm_email"/>
<property name="lkm_qq" column="lkm_qq"/>
<property name="lkm_position" column="lkm_position"/>
<property name="lkm_memo" column="lkm_memo"/>
<!--
配置多方:
name JavaBean中的屬性
class 屬性對應類的類路徑
column 表的外鍵字段(該文件上述配置中並沒有配置表的外鍵)
-->
<many-to-one name="customer" class="com.xiaoming.day01.domain.Customer" column="lkm_cust_id"/>
</class>
</hibernate-mapping>
操作客戶和聯繫人的數據
進行雙向關聯進行數據的保存(不設置級聯)
/**
* 最麻煩的方式雙向關聯的方式 保存數據
*/
@Test
public void fun1(){
Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();
//保存客戶和聯繫人的數據
Customer customer = new Customer();
customer.setCust_name("測試雙向關聯");
//創建兩個聯繫人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
//雙向關聯 客戶關聯聯繫人 聯繫人呢關聯客戶
Set<Linkman> linkmans = customer.getLinkmans();
linkmans.add(l1);
linkmans.add(l2);
l1.setCustomer(customer);
l2.setCustomer(customer);
//保存數據
session.save(customer);
session.save(l1);
session.save(l2);
//提交
transaction.commit();
}
@Test
//爲客戶增加聯繫人
public void fun2(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
//1> 獲得要操作的客戶對象
Customer c = session.get(Customer.class,1l);
//2> 創建聯繫人
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("郝強勇");
//3> 將聯繫人添加到客戶,將客戶設置到聯繫人中
c.getLinkMens().add(lm1);
lm1.setCustomer(c);
//4> 執行保存
session.save(lm1);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
}
@Test
//爲客戶刪除聯繫人
public void fun3(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
//1> 獲得要操作的客戶對象
Customer c = session.get(Customer.class,1l);
//2> 獲得要移除的聯繫人
LinkMan lm = session.get(LinkMan.class, 3l);
//3> 將聯繫人從客戶集合中移除
c.getLinkMens().remove(lm);
lm.setCustomer(null);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
}
級聯的取值(cascade的取值)
1. 級聯操作是指當主控方執行保存更新或者刪除操作時,其關聯對象(被控方)也執行相同的操作
* 在映射文件中配置 cascade屬性來設置是否採取級聯操作
* 級聯操作對各種關聯關係(3種)都是有效的
* 級聯保存是方向性
2. 需要大家掌握的取值如下
* none -- 不使用級聯
* save-update -- 級聯保存或更新
* delete -- 級聯刪除
* delete-orphan -- 孤兒刪除.(注意:只能應用在一對多關係)
* all -- 除了delete-orphan的所有情況.(包含save-update delete)
* all-delete-orphan -- 包含了delete-orphan的所有情況.(包含save-update delete delete-orphan)
3. 孤兒刪除(孤子刪除),只有在一對多的環境下才有孤兒刪除
* 在一對多的關係中,可以將一的一方認爲是父方.將多的一方認爲是子方.孤兒刪除:在解除了父子關係的時候.將子方記錄就直接刪除。
* <many-to-one cascade="delete-orphan" />
級聯操作
1. 測試:如果現在代碼只插入其中的一方的數據
* 如果只保存其中的一方的數據,那麼程序會拋出異常。(瞬時態的對象沒轉成持久態的對象)
* 如果想完成只保存一方的數據,並且把相關聯的數據都保存到數據庫中,那麼需要配置級聯!!
. 級聯保存效果
* 級聯保存:保存一方同時可以把關聯的對象也保存到數據庫中!!
* 使用cascade="save-update"
級聯保存或更新 sascade = save-update
/**
* 級聯保存:保存客戶,級聯聯繫人 (方向性)
* 在Customer的配置文件中配置級聯屬性 customer是主控方,linkman是被控方
* 級聯客戶同理
*/
@Test
public void run3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
// 保存客戶和聯繫人的數據
//創建一個客戶
Customer c1 = new Customer();
c1.setCust_name("美美");
// 創建2個聯繫人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
// 單向關聯 在customer.hbm.xml中配置級聯屬性
//cascade="save-update"
c1.getLinkmans().add(l1);
c1.getLinkmans().add(l2);
// 保存數據
// 將c1設置爲持久態
session.save(c1);
tr.commit();
}
/**
* 深入探索雙方之間的關係-1
*/
@Test
public void run5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
// 保存客戶和聯繫人的數據
Customer c1 = new Customer();
c1.setCust_name("美美");
// 創建2個聯繫人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
l1.setCustomer(c1);
c1.getLinkmans().add(l2);
//兩邊映射文件都配置了級聯屬性
//保存聯繫人(l1)就把級聯的客戶保存進去了
//而客戶還級聯了l2所以就都保存進去了
session.save(l1);
tr.commit();
}
/**
* 深入探索雙方之間的關係-2
*/
@Test
public void run5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
// 保存客戶和聯繫人的數據
Customer c1 = new Customer();
c1.setCust_name("美美");
// 創建三個聯繫人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
Linkman l3 = new Linkman();
l3.setLkm_name("熊三");
//建立關係
l1.setCustomer(c1);
c1.getLinkmans().add(l2);
c1.getLinkmans().add(l3);
// 雙方都配置了級聯屬性
//l1級聯了c1,c1級聯了l2,l3
session.save(l1) // 發送4條 insert語句
sesison.save(c1) // 發送3條 insert語句
session.sace(l2) // 發送1條 insert語句
tr.commit();
}
級聯刪除(慎用)
1. 先來給大家在數據庫中演示含有外鍵的刪除客戶功能,那麼SQL語句是會報出錯誤的
* 例如:delete from customers where cid = 1;
2. 如果使用Hibernate框架直接刪除客戶的時候,測試發現是可以刪除的
*hibernate會先讓外鍵=null,然後再刪除
*刪除客戶但是聯繫人數據沒刪 只是外鍵沒了 這些數據也沒意義
3. 上述的刪除是普通的刪除,那麼也可以使用級聯刪除,注意:級聯刪除也是有方向性的!!
* <many-to-one cascade="save-update,delete" />
@Test
//測試刪除客戶時,級聯刪除客戶下的聯繫人
//cascade:delete
public void fun2(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//級聯刪除必須是先查詢再刪除
//因爲查詢到客戶,這個時候客戶的聯繫人的集合就會有數據
Customer c = session.get(Customer.class,1l);
//2>調用delete刪除客戶
session.delete(c);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
}
級聯關係進階 inverse屬性
1. 先測試雙方都維護外鍵的時候,會產生多餘的SQL語句。
* 想修改客戶和聯繫人的關係,進行雙向關聯,雙方都會維護外鍵,會產生多餘的SQL語句。
* 客戶(一的一方)維護外鍵有點多餘
* inverse屬性可以優化sql語句提高性能
* 產生的原因:session的一級緩存中的快照機制,會讓雙方都更新數據庫,產生了多餘的SQL語句。
2. 如果不想產生多餘的SQL語句,那麼需要一方來放棄外鍵的維護!
* 在<set>標籤上配置一個inverse=”true”.true:放棄.false:不放棄.默認值是false
* <inverse="true">
* 肯定是也只能是一的哪一方放棄外鍵維護,以爲多的哪一方外鍵是它自己的屬性
@Test
//保存客戶 以及客戶 下的聯繫人
public void fun1(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
Customer c = new Customer();
c.setCust_name("傳智播客");
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("黎活明");
LinkMan lm2 = new LinkMan();
lm2.setLkm_name("劉悅東");
//表達一對多,客戶下有多個聯繫人.
// 如果客戶放棄維護與聯繫人的關係. 維護關係的代碼可以省略
//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();
}
cascade和inverse的區別
1. cascade用來級聯操作(保存、修改和刪除)
是用來簡化代碼的
2. inverse用來維護外鍵的
是用來提高效率的
3. 一對多的情況下 我們通常將一方設置inverse 將多放設置 cascade
一的一方放棄主鍵維護另外設置級聯刪除
多對多(用戶和角色爲例)
多對多的建表原則
JavaWEB的多對多:
一個用戶對應多個角色 ,一個角色對應多個用戶
很明顯多對多之間需要建立中間表
設立兩個外鍵 分別指向兩張表的主鍵
Hibernate框架:
只需要編寫2個JavaBean編寫兩個配置文件
中間表會自動生成
多對多JavaBean的編寫
- 用戶的JavaBean代碼如下
public class User {
private Long user_id;
private String user_code;
private String user_name;
private String user_password;
private String user_state;
private Set<Role> roles = new HashSet<Role>();
}
- 角色的JavaBean代碼如下
public class Role {
private Long role_id;
private String role_name;
private String role_memo;
private Set<User> users = new HashSet<User>();
}
多對多映射配置文件
- 用戶的映射配置文件如下
<class name="com.itheima.domain.User" table="sys_user">
<id name="user_id" column="user_id">
<generator class="native"/>
</id>
<property name="user_code" column="user_code"/>
<property name="user_name" column="user_name"/>
<property name="user_password" column="user_password"/>
<property name="user_state" column="user_state"/>
<!--
set標籤
name 關聯的另一方集合的屬性名稱
table 中間表的名稱
key標籤
coiumn 當前對象在中間表中的外鍵名稱
many-tomany標籤
class 關聯另一方的類的全路徑
column 關聯的另一方在中間表的外鍵名稱
-->
<!--這裏的配置中間表已全部維護-->
<set name="roles" table="sys_user_role">
<key column="user_id"/>
<many-to-many class="com.itheima.domain.Role" column="role_id"/>
</set>
</class>
- 角色的映射配置文件如下
<class name="com.itheima.domain.Role" table="sys_role">
<id name="role_id" column="role_id">
<generator class="native"/>
</id>
<property name="role_name" column="role_name"/>
<property name="role_memo" column="role_memo"/>
<set name="users" table="sys_user_role">
<key column="role_id"/>
<many-to-many class="com.itheima.domain.User" column="user_id"/>
</set>
</class>
注意多對多進行雙向關聯的時候:必須有一方去放棄外鍵維護權
多對多雙向關聯
@Test
//保存員工以及角色
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 創建兩個 User
User u1 = new User();
u1.setUser_name("郝強勇");
User u2 = new User();
u2.setUser_name("金家德");
// 創建兩個 Role
Role r1 = new Role();
r1.setRole_name("保潔");
Role r2 = new Role();
r2.setRole_name("保安");
<!--
如果多對多建立了雙向關聯,一定要有一方放棄外鍵維護
一般是由被動方放棄,用戶主動選擇角色,角色是
被動選擇的
其實不放棄也可以,我們可以只描述其中一張表中的內容
只執行下面的 "用戶表達關係" "角色表達式"
但是都得持久化
-->
<!--
之所以要一方放棄維護外鍵,是因爲不放棄會報錯因爲
多對多關聯維護外鍵是想中間表插入數據,雙方都維護
相同的數據插入兩次,但是插入的還都是主鍵信息,不能重複,
所以會保錯
而一對多不報錯的原因是因爲外鍵維護是修改(先執行的insert)的而不是
直接insert
-->
//3> 用戶表達關係
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r1);
u2.getRoles().add(r2);
//4> 角色表達關係
r1.getUsers().add(u1);
r1.getUsers().add(u2);
r2.getUsers().add(u1);
r2.getUsers().add(u2);
//5> 調用Save方法一次保存
session.save(u1);
session.save(u2);
session.save(r1);
session.save(r2);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
}
多對多級聯操作
@Test
//保存員工以及角色
public void fun1(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
//1> 創建兩個 User
User u1 = new User();
u1.setUser_name("郝強勇");
User u2 = new User();
u2.setUser_name("金家德");
//2> 創建兩個 Role
Role r1 = new Role();
r1.setRole_name("保潔");
Role r2 = new Role();
r2.setRole_name("保安");
//3> 用戶表達關係
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r1);
u2.getRoles().add(r2);
//4> 角色表達關係
r1.getUsers().add(u1);
r1.getUsers().add(u2);
r2.getUsers().add(u1);
r2.getUsers().add(u2);
//5> 調用Save方法一次保存
session.save(u1);
session.save(u2);
//沒錯配置級聯之後就少了這麼兩句
//session.save(r1);
//session.save(r2);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
@Test
//爲郝強勇新增一個角色
public void fun3(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
//1> 獲得郝強勇用戶
User user = session.get(User.class, 1l);
//2> 創建公關角色
Role r = new Role();
r.setRole_name("男公關");
//3> 將角色添加到用戶中
user.getRoles().add(r);
//4> 將角色轉換爲持久化 級聯之後就不用了
//session.save(r);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
}
@Test
//爲郝強勇解除一個角色
public void fun4(){
//1 獲得session
Session session = HibernateUtils.openSession();
//2 開啓事務
Transaction tx = session.beginTransaction();
//-------------------------------------------------
//3操作
//1> 獲得郝強勇用戶
User user = session.get(User.class, 1l);
//2> 獲得要操作的角色對象(保潔,保安)
Role r1 = session.get(Role.class, 1l);
Role r2 = session.get(Role.class, 2l);
//3> 將角色從用戶的角色集合中移除
user.getRoles().remove(r1);
user.getRoles().remove(r2);
//-------------------------------------------------
//4提交事務
tx.commit();
//5關閉資源
session.close();
}