一對多
表之間關系
一對多
- 一個部門有多個員工,一個員工只能屬於某一個部門
- 一個班級有多個學生,一個學生只能屬於一個班級
多對多
- 一個老師教多個學生,一個學生可以被多個老師教
- 一個學生可以先擇多門課程,一門課程可以被多個學生選擇
- 一個用戶可以選擇多個角色,一個角色也可以被多個用戶選擇
一對一
- 一個公司只能對應一個注冊地址
表之間關系建表原則
一對多
在多的一方創建一個外鍵,指向一的一方的主鍵
多對多
創建一個中間表,中間表至少有兩個字段,分別作為外鍵指向多對多雙方的主鍵
一對一
唯一外鍵對應
主鍵對應
一對多關系配置
建立表
創建表的 hbm.xml文件時,有外鍵可不創建列的映射
主表為客戶(Customer),從表為聯系人(Linkman)
銷售聯系人(linkman),一個聯系人只能屬於某一個客戶
CREATE TABLE `linkman` (
`link_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '聯系人編號(主鍵)',
`link_name` varchar(16) DEFAULT NULL COMMENT '聯系人姓名',
`link_cust_id` bigint(32) NOT NULL COMMENT '客戶id',
`link_gender` char(1) DEFAULT NULL COMMENT '聯系人性別',
`link_phone` varchar(16) DEFAULT NULL COMMENT '聯系人辦公電話',
`link_mobile` varchar(16) DEFAULT NULL COMMENT '聯系人手機',
`link_email` varchar(64) DEFAULT NULL COMMENT '聯系人郵箱',
`link_qq` varchar(16) DEFAULT NULL COMMENT '聯系人qq',
`link_position` varchar(16) DEFAULT NULL COMMENT '聯系人職位',
`link_memo` varchar(512) DEFAULT NULL COMMENT '聯系人備注',
PRIMARY KEY (`link_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
客戶(customer),一個客戶可以有多個聯系人
CREATE TABLE `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_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;
關系圖
建立ORM
實體類與數據庫中建立字段 與 關系
Customer 實體類(一個客戶可以有多個聯系人)
package com.myxq.domain;
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Getter@Setter
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_phone;
private String cust_mobile;
//建立一個客戶可以有多個聯系人
//放置多的一方的集合,hibernate默認使用的是Set集合
//如果使用List的話,它要對List進行排列,在表中要多建這一列,用來排序
//一般使用的都是Set集合
//現在是雙向關聯,從客戶能查聯系人,從聯系人也能查客戶
private Set<Linkman> linkmens = new HashSet<>();
@Override
public String toString() {
return "Customer{" +
"cust_id=" + cust_id +
", cust_name='" + cust_name + '\'' +
", cust_source='" + cust_source + '\'' +
", cust_industry='" + cust_industry + '\'' +
", cust_level='" + cust_level + '\'' +
", cust_phone='" + cust_phone + '\'' +
", cust_mobile='" + cust_mobile + '\'' +
'}';
}
}
Linkman 實體類(一個聯系人只能屬於一個客戶)
package com.myxq.domain;
import lombok.Getter;
import lombok.Setter;
@Getter@Setter
public class Linkman {
private Long link_id;
private String link_name;
private String link_gender;
private String link_phone;
private String link_mobile;
private String link_email;
private String link_qq;
private String link_position;
private String link_memo;
private String link_cust_id;
//一個聯系人只對應一個客戶
private Customer customer;
@Override
public String toString() {
return "Linkman{" +
"link_id=" + link_id +
", link_name='" + link_name + '\'' +
", link_gender='" + link_gender + '\'' +
", link_phone='" + link_phone + '\'' +
", link_mobile='" + link_mobile + '\'' +
", link_email='" + link_email + '\'' +
", link_qq='" + link_qq + '\'' +
", link_position='" + link_position + '\'' +
", link_memo='" + link_memo + '\'' +
", link_cust_id='" + link_cust_id + '\'' +
", customer=" + customer +
'}';
}
}
添加配置文件
1.客戶(Customer)實體類的配置文件
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>
<class name="com.myxq.domain.Customer" table="customer" >
<!--建立類屬性哪一個是主鍵 還要跟數據庫當中主鍵進行對象-->
<id name="cust_id" column="cust_id" >
<generator class="native"/>
</id>
<!--建立類中的普通屬性與數據庫當中的表字段的映射,關聯-->
<property name="cust_name" column="cust_name" />
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
<!--一對多-->
<set name="linkmens" cascade="save-update,delete" inverse="true"><!--set屬性名稱-->
<key column="link_cust_id"></key><!--外鍵-->
<one-to-many class="com.myxq.domain.Linkman"></one-to-many>
</set>
</class>
</hibernate-mapping>
2.聯系人(LinkMan)實體類配置文件
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>
<class name="com.myxq.domain.Linkman" table="linkman" >
<!--建立類屬性哪一個是主鍵 還要跟數據庫當中主鍵進行對象-->
<!-- 設置主鍵與OID的對應關系 -->
<id name="link_id" column="link_id" >
<generator class="native"/>
</id>
<!--建立類中的普通屬性與數據庫當中的表字段的映射 注意:外鍵不用設置-->
<property name="link_name" column="link_name" />
<property name="link_gender" column="link_gender"/>
<property name="link_phone" column="link_phone"/>
<property name="link_mobile" column="link_mobile"/>
<property name="link_email" column="link_email"/>
<property name="link_qq" column=" link_qq"/>
<property name="link_position" column=" link_position"/>
<property name="link_memo" column=" link_memo"/>
<!--
many-to-one:配置多對一
name:一的一方對象屬性名稱
class:一的一方類的全路徑
column:多的一方表的外鍵名稱
-->
<many-to-one name="customer" cascade="save-update" class="com.myxq.domain.Customer" column="link_cust_id"/>
</class>
</hibernate-mapping>
在hibernate.cfg.xml中的
標簽里,添加核心配置文件
<!--加載映射文件-->
<mapping resource="com/myxq/domain/customer.hbm.xml" />
<mapping resource="com/myxq/domain/linkman.hbm.xml" />
<mapping resource="com/myxq/domain/role.hbm.xml" />
<mapping resource="com/myxq/domain/user.hbm.xml" />
引入工具類
HibernateUtil.java
package com.myxq.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
//1.加載配置文件
Configuration configure = new Configuration().configure();
//configure.addResource("com/myxq/domain/Customer.hbm.xml");
//2.創建sessionFactory --JDBC 連接池
sessionFactory = configure.buildSessionFactory();
}
public static Session openSession(){
Session session = sessionFactory.openSession();
return session;
}
public static Session getCurrentSession(){
Session session = sessionFactory.getCurrentSession();
return session;
}
}
編寫測試類
級聯操作
問題
在兩張表建立一對多關系時,如果只保存一邊的對象,就會發異常
示例
什么是級聯
在操作一個對象的時候,是否會操作其關聯的對象。
級聯分類
級聯保存或更新
級聯刪除
級聯是有方向性
在操作一的一方,是否會操作多的一方
操作多的一方時, 是否會操作一的一方
級聯保存或更新
級聯保存
操作的主體是誰,就要在誰的映射配置文件當中進行配置
在開始配置的set當中添加一個新的屬性cascade="save-update"
在多的一方添加級聯
再去運行,就不會報異常,兩條記錄都會被添加
在一的一方添加級聯
對象導航
兩方如果都加了級聯,這種我們也稱為雙向導航
設置雙向導航時,當對象存在關系時, 就會做出對應的操作
級聯更新
級聯刪除
刪除一邊數據時,同時將另一邊的數據一並刪除
不設置級聯刪除
默認:先把外鍵改為空,然后再刪除
發送的SQL語句
設置級聯刪除
示例代碼
配置文件
在雙向級聯的過程當中,會產生一些多余的sql語句
原因
當雙向維護時,兩都都維護了外鍵,當做更新操作時, 兩邊的外鍵都要去修改
解決辦法
1.使用單向維護
有些地方還是會有問題
2.一方放棄維護權
在一的一方放棄外鍵維護權
在配置文件當中添加一個inverse="false/true"
true為放棄外鍵維護權,false為不放棄外鍵維護權
cascade與inverse
cascade控制有沒有關聯對象
inverse控制有沒有外鍵
示例
lazy懶加載(默認值是proxy,不自動獲取外鍵對象)
改成false,獲取外鍵對象
在linkman.hbm.xml
<many-to-one name="customer" class="com.myxq.domain.Customer" column="link_cust_id" lazy="false"/>
級聯保存或更新(解決 瞬時對象異常,只保存一邊)
在customer.hbm.xml
<set name="linkmens" cascade="save-update,delete" inverse="true">
<!--set屬性名稱-->
<key column="link_cust_id"></key><!--外鍵-->
<one-to-many class="com.myxq.domain.Linkman">
</one-to-many>
</set>
多對多
多對多關系配置
建立表
用戶表,一個用戶可以有多個角色
CREATE TABLE `user` (
`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
`user_code` varchar(32) NOT NULL COMMENT '用戶賬號',
`user_name` varchar(64) NOT NULL COMMENT '用戶名稱',
`user_password` varchar(32) NOT NULL COMMENT '用戶密碼',
`user_state` char(1) NOT NULL COMMENT '1:正常,0:暫停',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
角色表,一個角色可以被多個用戶選擇
CREATE TABLE `role` (
`role_id` bigint(32) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) NOT NULL COMMENT '角色名稱',
`role_memo` varchar(128) DEFAULT NULL COMMENT '備注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
關系圖
建立ORM
- 用戶
- 角色
Role.java
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.Set;
@Setter@Getter
public class Role {
private Long role_id;
private String role_name;
private String role_memo;
//角色下面的所有用戶
private Set<User> users = new HashSet<>();
}
添加配置文件
用戶
角色
在核心配置文件當中添加兩個新配置
編寫測試類
單向維護
雙向維護
雙向維護時,必須要有一方放棄外鍵維護
如果兩邊都有維護的話, 就會有重復的的記錄,由於關系表是兩個字段作為共同主鍵,不能有相同的記錄
解決辦法
通常都是讓被動方放棄,用戶選角色,角色為被動方
多對多的級聯操作和一對多的級聯操作是一樣的
多對多的操作
關系的操作,只需要操作集合,就可以操作它們之間的關系
給用戶添加一個新的角色
修改一個用戶的角色
刪除角色
查詢方式
OID查詢
什么是OID查詢
- 根據對象的OID主鍵進行檢索
OID查詢方式
- get方法
- load方法
對象導航查詢
什么是對象導航檢索
- Hibernate根據一個已經查詢到的對象,獲得其關聯的對象的一種查詢方式
- 先查詢到聯系人,就可以通過聯系人獲取聯系人所關聯的客戶對象
有點像級聯查詢
HQL
什么是HQL
- HQL查詢:Hibernate Query Language,Hibernate的查詢語言
- 是一種面向對象的方式的查詢語言,語法類似SQL。
- 通過session.createQuery(),用於接收一個HQL進行查詢方式。
- 注意:使用時,不能用*,對於表,要采用別名查詢
查詢
簡單查詢
別名查詢
排序查詢
條件查詢
位置綁定:根據參數的位置進行綁定條件
名稱綁定:把參數對應的值起一個名稱 再去設置名稱
投影查詢
查詢對象的某個或某些屬性
單個屬性
多個屬性
查詢多個屬性,封裝到對象當中
要在類中,提供構造方法
分頁查詢
統計查詢
查詢的結構只有一個
分組查詢
多表查詢
普通內連接
迫切內連接
通過hibernate將另一個對象的數據,封裝該對象中
在普通內連接inner join 后添加一個關鍵字fetch
QBC
什么是QBC
Query By Criteria,條件查詢。是一種更加面向對象化的查詢的方式。
查詢
簡單查詢
排序查詢
分頁查詢
條件查詢
條件
= eq
> gt
> = ge
< lt
<= le
<> ne
like
in
and
or
單個條件
多個條件
統計查詢
離線條件查詢
- 脫離Session,添加條件
- 可以在外部提前使用DetachedCriteria對象提交設置好條件
- 最后再綁定到session當中