layout: post
title: hibernate--多表
subtitle: 多表操作
date: 2018-05-23
author: ZL
header-img: img/20180523.jpg
catalog: true
tags:
- hibernate
- 多表
一對多多對一
數據庫中的一對多,多對一
指的是兩個數據庫表之間的關系,比如一個數據下面的例子中,一個表是存放客戶的表,另一個表存放聯系人。然后一個客戶會有多個聯系人,即一個客戶和多個聯系人綁定,那么對于客戶的表就是一對多,對于聯系人表就是多對一。
如何將兩個表的關系表達出來,在數據庫中有一個叫外鍵的東西。在一對多中處在“多”位置的那個表中要表明外鍵是“一”的表的某個字段。
hibernate中處理一對多多對一的操作
-
首先看一下兩個表的內容。
--客戶表的內容 CREATE TABLE `cst_customer` ( `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客戶編號(主鍵)', `cust_name` VARCHAR(32) NOT NULL COMMENT '客戶名稱(公司名稱)', `cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客戶信息來源', `cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客戶所屬行業', `cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客戶級別', `cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '聯系人', `cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定電話', `cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移動電話', PRIMARY KEY (`cust_id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- 聯系人表的內容 -- 在這里表明了外鍵叫“lkm_cust_id”,它指向了客戶表的“cust_id”字段。 CREATE TABLE `cst_linkman` ( `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '聯系人編號(主鍵)', `lkm_name` varchar(16) DEFAULT NULL COMMENT '聯系人姓名', `lkm_cust_id` bigint(32) NOT NULL COMMENT '客戶id', `lkm_gender` char(1) DEFAULT NULL COMMENT '聯系人性別', `lkm_phone` varchar(16) DEFAULT NULL COMMENT '聯系人辦公電話', `lkm_mobile` varchar(16) DEFAULT NULL COMMENT '聯系人手機', `lkm_email` varchar(64) DEFAULT NULL COMMENT '聯系人郵箱', `lkm_qq` varchar(16) DEFAULT NULL COMMENT '聯系人qq', `lkm_position` varchar(16) DEFAULT NULL COMMENT '聯系人職位', `lkm_memo` varchar(512) DEFAULT NULL COMMENT '聯系人備注', PRIMARY KEY (`lkm_id`), KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`), CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-
創建關于兩個表的Bean類
客戶的bean類:package cn.itheima.domain; import java.util.HashSet; import java.util.Set; public class Customer { private Long cust_id; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_linkman; private String cust_phone; private String cust_mobile; private Set<LinkMan> linkMens = new HashSet<LinkMan>(); public Long getCust_id() { return cust_id; } public void setCust_id(Long cust_id) { this.cust_id = cust_id; } public String getCust_name() { return cust_name; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } public String getCust_source() { return cust_source; } public void setCust_source(String cust_source) { this.cust_source = cust_source; } public String getCust_industry() { return cust_industry; } public void setCust_industry(String cust_industry) { this.cust_industry = cust_industry; } public String getCust_level() { return cust_level; } public void setCust_level(String cust_level) { this.cust_level = cust_level; } public String getCust_linkman() { return cust_linkman; } public void setCust_linkman(String cust_linkman) { this.cust_linkman = cust_linkman; } public String getCust_phone() { return cust_phone; } public Set<LinkMan> getLinkMens() { return linkMens; } public void setLinkMens(Set<LinkMan> linkMens) { this.linkMens = linkMens; } public void setCust_phone(String cust_phone) { this.cust_phone = cust_phone; } public String getCust_mobile() { return cust_mobile; } public void setCust_mobile(String cust_mobile) { this.cust_mobile = cust_mobile; } @Override public String toString() { return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]"; } }
linkMens字段是客戶表中沒有的,這里寫出來linkMens并給他設置set,get是因為一個客戶對應多個聯系人,在后面的代碼中有類似將聯系人和客戶綁定的操作,需要用到linkMens字段。
聯系人的bean類:
package cn.itheima.domain;
//聯系人實體
public class LinkMan {
private Long lkm_id;
private Character lkm_gender;
private String lkm_name;
private String lkm_phone;
private String lkm_email;
private String lkm_qq;
private String lkm_mobile;
private String lkm_memo;
private String lkm_position;
//表達多對一關系
private Customer customer ;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Long getLkm_id() {
return lkm_id;
}
public void setLkm_id(Long lkm_id) {
this.lkm_id = lkm_id;
}
public Character getLkm_gender() {
return lkm_gender;
}
public void setLkm_gender(Character lkm_gender) {
this.lkm_gender = lkm_gender;
}
public String getLkm_name() {
return lkm_name;
}
public void setLkm_name(String lkm_name) {
this.lkm_name = lkm_name;
}
public String getLkm_phone() {
return lkm_phone;
}
public void setLkm_phone(String lkm_phone) {
this.lkm_phone = lkm_phone;
}
public String getLkm_email() {
return lkm_email;
}
public void setLkm_email(String lkm_email) {
this.lkm_email = lkm_email;
}
public String getLkm_qq() {
return lkm_qq;
}
public void setLkm_qq(String lkm_qq) {
this.lkm_qq = lkm_qq;
}
public String getLkm_mobile() {
return lkm_mobile;
}
public void setLkm_mobile(String lkm_mobile) {
this.lkm_mobile = lkm_mobile;
}
public String getLkm_memo() {
return lkm_memo;
}
public void setLkm_memo(String lkm_memo) {
this.lkm_memo = lkm_memo;
}
public String getLkm_position() {
return lkm_position;
}
public void setLkm_position(String lkm_position) {
this.lkm_position = lkm_position;
}
}
這里也多了一個屬性叫做customer,是為了后面的綁定操作。和上面的linkMens作用一樣。
- 創建hbm.xml
客戶表的Bean類的hbm.xml:Customer.hbm.xml
<?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 package="cn.itheima.domain" >
<class name="Customer" table="cst_customer" >
<id name="cust_id" >
<generator class="native"></generator>
</id>
<property name="cust_name" column="cust_name" ></property>
<property name="cust_source" column="cust_source" ></property>
<property name="cust_industry" column="cust_industry" ></property>
<property name="cust_level" column="cust_level" ></property>
<property name="cust_linkman" column="cust_linkman" ></property>
<property name="cust_phone" column="cust_phone" ></property>
<property name="cust_mobile" column="cust_mobile" ></property>
<!-- 集合,一對多關系,在配置文件中配置 -->
<!--
name屬性:集合屬性名
column屬性: 外鍵列名
class屬性: 與我關聯的對象完整類名
-->
<set name="linkMens">
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
</class>
</hibernate-mapping>
*這里指出set以及其內部的key和one-to-many
聯系人表的Bean類的hbm.xml:LinkMan.hbm.xml
<?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 package="cn.itheima.domain" >
<class name="LinkMan" table="cst_linkman" >
<id name="lkm_id" >
<generator class="native"></generator>
</id>
<property name="lkm_gender" ></property>
<property name="lkm_name" ></property>
<property name="lkm_phone" ></property>
<property name="lkm_email" ></property>
<property name="lkm_qq" ></property>
<property name="lkm_mobile" ></property>
<property name="lkm_memo" ></property>
<property name="lkm_position" ></property>
<!-- 多對一 -->
<!--
name屬性:引用屬性名
column屬性: 外鍵列名
class屬性: 與我關聯的對象完整類名
-->
<many-to-one name="customer" column="lkm_cust_id" class="Customer" >
</many-to-one>
</class>
</hibernate-mapping>
在這里指出many-to-one就可以了
這里的聯系人表property只有name沒有指明column,不知道為什么,不知道是一定不能指明還是指明不指明都可以
- 在hibernate.cfg.xml中把兩個hbm.xml的位置指出來。
<mapping resource="cn/itheima/domain/Customer.hbm.xml" />
<mapping resource="cn/itheima/domain/LinkMan.hbm.xml"/>
- 代碼操作
@Test
//保存客戶 以及客戶 下的聯系人
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction transaction = session.beginTransaction();
//1.客戶創建對象(一個)
Customer c= new Customer();
c.setCust_name("xxx");
//2.創建聯系人對象(多個)
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("111");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("222");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("333");
//相互綁定
c.getLinkMens().add(linkMan1);
c.getLinkMens().add(linkMan2);
c.getLinkMens().add(linkMan3);
//相互綁定
linkMan1.setCustomer(c);
linkMan2.setCustomer(c);
linkMan3.setCustomer(c);
session.save(c);
session.save(linkMan1);
session.save(linkMan2);
session.save(linkMan3);
transaction.commit();
session.close();
}
@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();
}
- 小總結一波
這種一對多和單個表的不同地方在于:
- bean類中需要加入對方類型的屬性,不然后面沒法綁定。
- hbm.xml中要指出一些一對多,多對一的信息。
- hibernate.cfg.xml要把所有的hbm.xml指明出來
- 代碼中也要相互綁定。
級聯操作
這可以簡化操作。
比如原來保存是這樣的。客戶和聯系人需要單獨保存。
session.save(c);
session.save(linkMan1);
session.save(linkMan2);
session.save(linkMan3);
有了級聯操作以后,就可以只保存客戶,和客戶掛鉤的三個聯系人會自動保存。
session.save(c);//一句就可以
在hbm.xml中配置cascade就可以了。
比如:
<set name="linkMens" cascade="delete" >
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
cascade有四個屬性值:
- delete:級聯刪除,刪除一個就刪除另一個
- save-update: 級聯保存更新,保存一個也保存另一個
- all:save-update+delete
- none:默認值,不級聯
兩個hbm.xml都可以配置,也可以只配置一個。比如配置了客戶表的save-update,那么保存客戶時,客戶的聯系人也保存了,配置聯系人的hbm.xml時,保存聯系人時,客戶也保存了,兩個都配置時,保存任意一個另一個也保存了。
級聯操作的delete有一個邏輯問題,最好不要用,比如一個客戶有三個聯系人,我想刪除聯系人a,但是因為delete的配置效果,會把a對應的客戶也刪除了,這是不符合預期的,因為聯系人b和c還在呢。如果客戶也配置了delete的話,客戶刪除的時候還會把b和c也刪除了,這就太不符合預期了。
外鍵關系維護
在默認情況下,一對多中,兩個表都會維護這個外鍵關系。而這個關系其實只要一方維護就可以。在“一”的那一方可以放棄維護,“多”的那一方是不能放棄的。
“一”的那一方放棄的方法就是在hbm.xml中配置inverse="true"。true 表示放棄維護,默認是false。這個配置的位置在:
<set name="linkMens" inverse="true">
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan" />
</set>
減少一方維護關系可以優化性能
要么兩個都維護關系,要么只“多”的一方維護關系(因為外鍵字段就在“多”的一方)。
多對多
多對多大同小異,兩個表都有外鍵,多對多會引入一個中間表。
注意點:
-
在兩個表的bean類中都要加入set集合,以及set集合的get,set方法。
比如在類A里面有:
private Set<B> users = new HashSet<B>();
在類B里面有:
private Set<A> users = new HashSet<A>();
兩個表的hbm.xml配置:
比如User:
<set name="roles" table="sys_user_role" cascade="save-update" >
<key column="user_id" ></key>
<many-to-many class="Role" column="role_id" ></many-to-many>
</set>
name:bean類中的set集合屬性的名稱
table:中間表的名字,自己取名,兩個xml一致即可。
column:我的外鍵名
class:和我多對多對應的那個類
column:和我多對多的那個外鍵
代碼使用:
//多對多關系操作
public class Demo {
@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();
}
注意:
這里如果兩方都表達關系,則需要在配置hbm.xml中有一方放棄維護,或者是兩方都維護關系,但是上述代碼只有一方表達關系,也就是上面的代碼第三步和第四步只留其一。
結論:
以后直接放棄一方維護關系,具體放棄哪一方要根據邏輯自己判斷。
@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);//user類如果使用了級聯保存這句代碼可以省略。
//-------------------------------------------------
//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();
}
}
多對多也有cascade級聯操作,這里更不能用delete。