Hibernate一對多,多對多操作
表與表之間的關係
一對多:
- 分類和商品關係,一個分類裡面有多個商品,一個商品只能屬於一個分類。
客戶和聯絡人是一對多關係:
聯絡人在人際交流中擔任著重要的角色,在銷售過程中,我們通常不是在最開始就聯絡到有決策權的高管,而有可能是接電話的前臺A、營銷人員B、客服C、技術人員D等等。這些人都是我們與企業保持交流的聯絡人。他們對產品的支援態度決定了產品是否能夠順利推送到高管面前。
通常銷售人員在跟進一個客戶時,會有一個或者多個聯絡人,這些聯絡人所處的職位決定了他們的角色。
- 客戶:與公司有業務往來,比如淘寶商家與淘寶就有業務往來,淘寶商家和淘寶談判想要讓自己的商品在淘寶上架,其中淘寶就是客戶。
- 聯絡人:為了和淘寶聯絡,淘寶公司裡面的員工等就是聯絡人。
也就是說客戶和聯絡人的關係是:
公司和公司員工的關係。
- 一個公司可以有多個員工,即一個客戶可以有多個聯絡人。
- 一個聯絡人只能有一個客戶,即一個員工只能有一個公司。
多對多:
- 訂單和商品關係,一個訂單裡面有多個商品,一個商品屬於多個訂單。
- 老師和學生,一個老師可以教多個學生,一個學生可以被多個老師教。
一對一:
Hibernate中的一對多操作(重點)
一對多對映配置:
以客戶和聯絡人為例:客戶是一,聯絡人是多。
第一步:建立兩個實體類,客戶和聯絡人:
第二步:讓兩個實體類之間互相表示。
- 讓客戶實體類裡面表示多個聯絡人。
一個客戶可以對應多個聯絡人。
這裡裝載聯絡人,裝載的容器用的是Set集合,而不是LinkedList,因為Set集合的特點是,值不能夠重複。
- 在聯絡人實體類裡面表示所屬客戶。
一個聯絡人只能屬於一個客戶。
//客戶的實體類:
public class Customer {
private Integer cid;
private String custName;
private String custLevel;
private String custSource;
private String custPhone;
private String custMobile;
private Set<LinkMan> linkMans=new HashSet<LinkMan>();
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String getCustMobile() {
return custMobile;
}
public void setCustMobile(String custMobile) {
this.custMobile = custMobile;
}
public Customer(String custName, String custLevel, String custSource, String custPhone,
String custMobile) {
super();
this.custName = custName;
this.custLevel = custLevel;
this.custSource = custSource;
this.custPhone = custPhone;
this.custMobile = custMobile;
}
public Customer() {
super();
}
@Override
public String toString() {
return "Customer [cid=" + cid + ", custName=" + custName + ", custLevel=" + custLevel + ", custSource="
+ custSource + ", custPhone=" + custPhone + ", custMobile=" + custMobile + "]";
}
}
//聯絡人實體類
public class LinkMan {
private Integer lid;
private String lname;
private String lgender;
private String lphone;
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Integer getLid() {
return lid;
}
public void setLid(Integer lid) {
this.lid = lid;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public String getLgender() {
return lgender;
}
public void setLgender(String lgender) {
this.lgender = lgender;
}
public String getLphone() {
return lphone;
}
public void setLphone(String lphone) {
this.lphone = lphone;
}
public LinkMan() {
super();
// TODO Auto-generated constructor stub
}
public LinkMan(String lname, String lgender, String lphone) {
super();
this.lname = lname;
this.lgender = lgender;
this.lphone = lphone;
}
@Override
public String toString() {
return "LinkMan [lid=" + lid + ", lname=" + lname + ", lgender=" + lgender + ", lphone=" + lphone + "]";
}
}
第三步:配置對映關係
- 一般一個實體類對應一個對映檔案
- 把對映最基本配置完成
- 在對映檔案中,配置一對多關係(重點)
在客戶的對映檔案中,表示所有的聯絡人
<class name="類的全路徑" table="表名">
<id name="id" column="cid">//實體類屬性名以及表字段名
<generator class="native"></generator>
</id>
<property name="custName"></property>
<property name="custLevel"></property>
<property name="custSource"></property>
<property name="custPhone"></property>
<property name="custMobile"></property>
<!--
在客戶對映檔案中,表示所有聯絡人
使用set標籤表示所有聯絡人
set標籤裡面有name屬性:屬性值寫,在客戶實體類裡面表示聯絡人的set集合名稱
-->
<set name="setLinkMan">
<!--
一對多建表,有外來鍵
hibernate機制:雙向維護外來鍵,在一和多兩方都配置外來鍵。
column屬性值:外來鍵名稱。這裡的外來鍵名稱可以隨便寫。
-->
<key column="clid"></key>
<!-- 客戶所有的聯絡人,class裡面寫聯絡人實體類全路徑 -->
<one-to-many class="聯絡人實體類全路徑" />
</set>
</class>
在聯絡人對映檔案中,表示所屬客戶
<class name="實體類的全路徑" table="表名">
<id name="lid"><!-- 當column不寫時,名稱和name的名稱一樣 -->
<generator class="native"></generator>
</id>
<property name="lname"></property>
<property name="lgender"></property>
<property name="lphone"></property>
<!--
表示聯絡人所屬客戶
name屬性:聯絡人實體類被裝載到Customer中,這裡寫customer名稱
class屬性:customer全路徑
column屬性:外來鍵名稱
-->
<many-to-one name="customer" class="customer全路徑" column="clid"></many-to-one>
</class>
第四步:建立核心配置檔案,把對映檔案引入到核心配置檔案中。
<mapping resource="cn/domarvel/entity/Customer.hbm.xml"/>
<mapping resource="cn/domarvel/entity/LinkMan.hbm.xml"/>
第五步:寫測試類:
最後:執行專案,讓表被建立。
可以看到結果,客戶表和聯絡人表進行了關聯,聯絡人引入客戶表的id作為外來鍵。
一對多級聯操作
這些操作Hibernate都幫你封裝好了。
級聯操作
級聯儲存:
- 新增一個客戶,為這個客戶添加了多個聯絡人。
//在沒有進行簡化前,需要將聯絡人和客戶的關係在程式碼行中進行說明。
@Test
public void showCreate(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=new Customer("百度", "vip", "網路", "110", "120");
LinkMan linkMan=new LinkMan("侯鬆", "男", "123");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);//注意在沒有進行配置簡化配置的時候還需要讓聯絡人實體類和客戶實體類關聯。
session.save(customer);
session.save(linkMan);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
簡化操作:
在對映檔案的客戶方,也就是一對多的一這方進行配置:
<set name="linkMans" cascade="save-update"><!-- 這裡配置了一個屬性cascade,值為save-update,表示對儲存和更新操作,做了簡化,不用對多的那方
設定和一的那方的關係。 -->
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//簡化後代碼
@Test
public void showCreate02(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=new Customer("騰訊", "svip", "網路", "110", "120");
LinkMan linkMan=new LinkMan("FireLang", "女", "123");
customer.getLinkMans().add(linkMan);//注意在這裡就只需要把聯絡人(多的那方)放入到客戶方(一的那方),就可以了。
session.save(customer);//最後儲存的時候就只需要儲存客戶方(一的那方)
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
級聯刪除:
- 刪除某一個客戶,這個客戶裡面的所有的聯絡人也刪除。
這裡就直接上簡化後的程式碼了。
<!-- 配置對映檔案 -->
<!-- 在一的那方配置刪除的簡化,當要想直接刪除一的那方,Hibernate自動幫你刪除相關聯的多的那方 -->
<set name="linkMans" cascade="save-update,delete"><!-- 在cascade裡面配置delete,多個值用","分割 -->
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
//測試程式碼
@Test
public void showDelete(){
//在以前我們要刪除一的那方,是先根據一的那方刪除多的那方,再刪除一的那方。
//現在Hibernate已經幫我們封裝好了,我們只需要配置好,再直接刪除一的那方就行了。
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=session.get(Customer.class, 2);
session.delete(customer);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
級聯修改:
操作:現在是讓一個聯絡人的客戶變成另外一個。我們需要修改的就是某一個員工的外來鍵值。
我們的普通更新操作:
@Test
public void showUpdate(){
//下面通過一個例子引入Hibernate中作更新操作時能夠使用的一個關鍵字,目的是能夠簡化更新操作
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
Customer customer=session.get(Customer.class, 1);
LinkMan linkMan=session.get(LinkMan.class, 3);
linkMan.setCustomer(customer);//因為這裡的資料是持久態的所以我們直接設定值後,Hibernate會幫我們自動更新資料。
customer.getLinkMans().add(linkMan);
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
}
//通過輸出底層sql程式碼可以知道
Hibernate:
select
customer0_.cid as cid1_1_0_,
customer0_.custName as custName2_1_0_,
customer0_.custLevel as custLeve3_1_0_,
customer0_.custSource as custSour4_1_0_,
customer0_.custPhone as custPhon5_1_0_,
customer0_.custMobile as custMobi6_1_0_
from
customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lid as lid1_2_0_,
linkman0_.lname as lname2_2_0_,
linkman0_.lgender as lgender3_2_0_,
linkman0_.lphone as lphone4_2_0_,
linkman0_.clid as clid5_2_0_
from
linkman linkman0_
where
linkman0_.lid=?
Hibernate:
select
linkmans0_.clid as clid5_2_0_,
linkmans0_.lid as lid1_2_0_,
linkmans0_.lid as lid1_2_1_,
linkmans0_.lname as lname2_2_1_,
linkmans0_.lgender as lgender3_2_1_,
linkmans0_.lphone as lphone4_2_1_,
linkmans0_.clid as clid5_2_1_
from
linkman linkmans0_
where
linkmans0_.clid=?
Hibernate:
update
linkman
set
lname=?,
lgender=?,
lphone=?,
clid=?
where
lid=?
Hibernate:
update
linkman
set
clid=?
where
lid=?
//在這裡進行了重複的修改外來鍵的操作,修改兩次的原因是Hibernate雙向維護外來鍵。
inverse屬性
- 在一對多裡面,當我們更新一對多的關係的時候,更新聯絡人所屬的客戶的時候。也就是上面的程式碼。我們設定
linkMan.setCustomer(customer);
。達到了更新聯絡人所屬關係。但是從上面的sql語句輸出的情況可以看出。有兩次的update操作。這兩次是怎麼來的呢???一次是我們自己寫的。linkMan.setCustomer(customer);
另外一次是Hibernate幫我們修改外來鍵關係的。所以這裡就重複了update語句。使得修改關係時效率降低了。(注意:當我們修改多的那方的時候,不是修改的所屬關係,而是其它的資料,那麼就不會有兩次更新。)那麼我們該怎麼解決更新兩次的問題呢???那這裡就得靠inverse屬性了。
解決方式:讓其中的一方不維護外來鍵。
- 一對多裡面,讓其中一方放棄外來鍵維護。
- 一個國家有總統,國家有很多人,總統不可能認識國家所有人,國家所有人可以認識總統。
- 即讓一的一方不維護外來鍵。
具體實現:
在放棄關係維護對映檔案中,進行配置,在set標籤上使用inverse屬性。(inverse:反向)
inverse:
- 預設值為false;
- true表示放棄關係維護
- false表示不放棄關係維護
- 目的:提高效能問題。
//在這裡不需要對java程式碼進行修改,只需要進行配置就可以了。這裡還是用的是上面的問題程式碼。
//在一的一方進行配置,目的,不讓一的一方反向維護
<set name="linkMans" cascade="save-update,delete" inverse="true">
<key column="clid"></key>
<one-to-many class="cn.domarvel.entity.LinkMan"/>
</set>
再次作關係更新操作,看一下執行結果:
Hibernate:
select
customer0_.cid as cid1_1_0_,
customer0_.custName as custName2_1_0_,
customer0_.custLevel as custLeve3_1_0_,
customer0_.custSource as custSour4_1_0_,
customer0_.custPhone as custPhon5_1_0_,
customer0_.custMobile as custMobi6_1_0_
from
customer customer0_
where
customer0_.cid=?
Hibernate:
select
linkman0_.lid as lid1_2_0_,
linkman0_.lname as lname2_2_0_,
linkman0_.lgender as lgender3_2_0_,
linkman0_.lphone as lphone4_2_0_,
linkman0_.clid as clid5_2_0_
from
linkman linkman0_
where
linkman0_.lid=?
Hibernate:
select
linkmans0_.clid as clid5_2_0_,
linkmans0_.lid as lid1_2_0_,
linkmans0_.lid as lid1_2_1_,
linkmans0_.lname as lname2_2_1_,
linkmans0_.lgender as lgender3_2_1_,
linkmans0_.lphone as lphone4_2_1_,
linkmans0_.clid as clid5_2_1_
from
linkman linkmans0_
where
linkmans0_.clid=?
Hibernate:
update
linkman
set
lname=?,
lgender=?,
lphone=?,
clid=?
where
lid=?
//可以看到這裡update只更新了一次
//這裡要注意:我們設定inverse=true,只是解決對更新所屬關係時帶來的效率問題。
Hibernate多對多操作
多對多對映配置
- 以使用者和角色為例
第一步:建立實體類,使用者和角色。
第二步:讓兩個實體類之間互相表示。
- 一個使用者裡面表示所有角色,使用set集合
public class User {
private Integer uid;
private String uname;
private String pword;
private Set<Role> roleSet=new HashSet<Role>();
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPword() {
return pword;
}
public void setPword(String pword) {
this.pword = pword;
}
public Set<Role> getRoleSet() {
return roleSet;
}
public void setRoleSet(Set<Role> roleSet) {
this.roleSet = roleSet;
}
@Override
public String toString() {
return "User [uid=" + uid + ", uname=" + uname + ", pword=" + pword + ", roleSet=" + roleSet + "]";
}
public User() {
super();
// TODO Auto-generated constructor stub
}
public User( String uname, String pword, Role role) {
super();
this.uname = uname;
this.pword = pword;
roleSet.add(role);
}
}
- 一個角色有多個使用者,使用set集合
public class Role {
private Integer rid;
private String rname;
private String rmemo;
private Set<Role> userSet=new HashSet<Role>();
public Set<Role> getUserSet() {
return userSet;
}
public void setUserSet(Set<Role> userSet) {
this.userSet = userSet;
}
public Integer getRid() {
return rid;
}
public void setRid(Integer rid) {
this.rid = rid;
}
public String getRname() {
return rname;
}
public void setRname(String rname) {
this.rname = rname;
}
public String getRmemo() {
return rmemo;
}
public void setRmemo(String rmemo) {
this.rmemo = rmemo;
}
@Override
public String toString() {
return "Role [rid=" + rid + ", rname=" + rname + ", rmemo=" + rmemo + "]";
}
public Role(String rname, String rmemo) {
super();
this.rname = rname;
this.rmemo = rmemo;
}
public Role() {
super();
// TODO Auto-generated constructor stub
}
}
第三步:配置對映關係
- 基本配置
配置多對多關係
在使用者裡面表示所有角色,使用set標籤
<?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="cn.domarvel.manytomany.Userb" table="userb">
<!-- table是要生成的表名 -->
<id name="uid">
<generator class="native"></generator>
</id>
<property name="uname"/>
<property name="pword"/>
<!-- property裡面的column屬性可以不用寫,不寫就預設和property裡面的name屬性值相同 -->
<set name="roleSet" table="user_role" cascade="save-update">
<!--
在使用者裡面表示角色,使用set標籤
name屬性:角色set集合名稱
table屬性:第三張表名稱
-->
<!--
注意如果還沒有在其它對映檔案中配置表名,那麼第一次配置就可以亂寫,
但是如果有一張表已經宣告說表名叫什麼了,那就不能夠再亂寫
-->
<key column="fuid"/>
<!--
key標籤裡面配置:
配置當前對映檔案在第三張表外來鍵名稱
-->
<many-to-many class="cn.domarvel.manytomany.Role" column="frid"></many-to-many>
<!--
class:角色實體類全路徑
column:角色在第三張表外來鍵名稱
-->
</set>
</class>
</hibernate-mapping>
- 在角色裡面表示所有使用者,使用set標籤
<?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="cn.domarvel.entity.Role" table="role">
<id name="rid">
<generator class="native"></generator>
</id>
<property name="rname"/>
<property name="rmemo"/>
<set name="userSet" table="user_role">
<key column="frid"></key>
<many-to-many class="cn.domarvel.entity.User" column="fuid"></many-to-many>
</set>
</class>
</hibernate-mapping>
第四步:在核心配置檔案中引入對映檔案
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<mapping resource="cn/domarvel/entity/Role.hbm.xml"/>
<mapping resource="cn/domarvel/entity/User.hbm.xml"/><!-- 引入對映配置檔案到核心配置檔案 -->
</session-factory>
</hibernate-configuration>
多對多級聯儲存
根據使用者儲存角色:
第一步:在使用者配置檔案中set標籤進行配置,cascade值save-update,意思是對級聯操作進行了簡化,比如說:當我一對多的時候,我們以前是先刪除多的那方再刪除一的那方,或者先儲存多的那方再儲存一的那方。但是現在不用瞭如果我們設定了save-update或者delete,那我們只需要刪除一的那方或者儲存一的那方就行了,但前提是必須要先把多的那方儲存到一的那方里面才可以使用。這也就是簡化操作。
<set name="roleSet" table="user_role" cascade="save-update"><!-- 注意cascade是配置到哪兒裡後,在儲存的時候就儲存哪兒一個被配置的實體。並且
另外一個實體就儲存到被配置的實體裡面。這裡配置的是User所以就是根據User儲存角色 -->
第二步:寫程式碼實現
- 建立使用者和角色物件,把角色放到使用者裡面,最終儲存使用者就可以了。
@Test
public void showAddManyToM(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
User user=new User("LangSheng", "123456",new Role("開發工程師", "專注於網頁前後臺開發,和影象演算法!!"));
user.getRoleSet().add(new Role("架構師", "進行程式開發架構!"));
User user2=new User("FireLang", "456123", new Role("狼神總部副總裁", "國際知名企業!!"));
user2.getRoleSet().add(new Role("影象演算法師", "通過程式碼對程式的演算法處理!以及識別!!"));
session.save(user);
session.save(user2);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}
多對多級聯刪除
對於級聯刪除
在完成上面級聯刪除的操作後,對於級聯刪除就變得很簡單了。
第一步:在set標籤中配置cascade屬性,值為delete;如果有多個值記得用”,”號隔開,比如cascade=”save-update,delete”;
第二步:直接進行刪除。
對於級聯刪除,在我們一對多的時候還可以用,也挺方便的。但是在我們的多對多的裡面就顯得有些雞肋了,因為,假如我們刪除使用者表裡面的某個使用者,那麼就會把該使用者和角色表裡面屬於該使用者角色屬性的角色以及第三張表涉及到該使用者的都會刪掉。而我們一般角色是不能夠刪除的,因為有可能其它使用者還有該角色,如果你把別人的角色刪掉了,那麼其它使用者和該角色在第三張表中的關係也會被刪掉的。
所以我們不推薦使用級聯刪除來操作多對多關係。
我們應該做的是維護第三張表,也就是使用者和角色的主鍵組成的一個外建表。
維護第三張表關係
使用者和角色多對多關係,維護關係通過第三張表維護
讓某個使用者有某個角色
第一步:根據id查詢使用者和角色
第二步:把角色放到使用者裡面
- 把角色物件放到使用者set集合
@Test
public void showCreateRelation(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
User user=session.get(User.class, 4);
Role role=session.get(Role.class, 6);
user.getRoleSet().add(role);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}
讓某個使用者沒有某個角色
@Test
public void showRemoveReation(){
Transaction transaction=null;
try {
Session session=HibernateUtils.getCurrentSession();
transaction=session.beginTransaction();
User user=session.get(User.class, 4);
Role role=session.get(Role.class, 6);
user.getRoleSet().remove(role);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
}