1 多表設計sql
l 在開發中,前期進行需求分析,需求分析提供E--R圖,根據ER圖編寫表結構。數據庫
l 表之間關係存在3種:一對多、多對多、一對一。(回顧)緩存
一對多:1表(主表)必須主鍵 和 多表(從表)必須外鍵,主表的主鍵 與 從表外鍵 造成主外鍵關係session
多對多:提供中間表(從表),提供2個字段(外鍵)分別對應兩個主表。app
一對一:???函數
l 面單對象描述 對象與對象 之間關係?【掌握】性能
一對多:客戶和訂單學習
private class Customer{fetch
//一對多:一個客戶 擁有 多個訂單優化
private Set<Order> orderSet;
}
private class Order{
//多對一:多個訂單 屬於 一個客戶
private Customer customer;
}
多對多:Student學生 和 Course課程
private class Student{
//多對多:多個學生(當前)學習 【不一樣課程】
private Set<Course> courseSet ...;
}
private class Course{
//多對多:多個課程 能夠被 【不一樣學生】學習
private Set<Student> student = ...;
}
一對一:公司company 和 地址address
private class Company{
private Address address;
}
private class Address{
private Company company;
}
public class Customer {
private Integer cid; private String cname;
//一對多:一個客戶(當前客戶) 擁有 【多個訂單】 // * 須要容器存放多個值,通常建議Set (不重複、無序) // * 參考集合:List、Map、Array等 // ** 建議實例化--使用方便 private Set<Order> orderSet = new HashSet<Order>(); |
public class Order { private Integer xid; private String price;
//多對一:多個訂單屬於【一個客戶】 private Customer customer; |
l Customer.hbm.xml
<class name="com.hibernate.b_onetomany.Customer" table="t_customer"> <id name="cid"> <generator class="native"></generator> </id> <property name="cname"></property>
<!-- 一對多:一個客戶(當前客戶) 擁有 【多個訂單】 1 肯定容器 set <set> 2 name肯定對象屬性名 3 肯定從表外鍵的名稱 4 肯定關係,及另外一個對象的類型 注意: 在hibernate中能夠只進行單向配置 每個配置項均可以完整的描述彼此關係。 通常狀況採用雙向配置,雙方均可以完成描述表與表之間關係。 --> <!-- 一對多:一個客戶(當前客戶) 擁有 【多個訂單】 --> <set name="orderSet" cascade="delete-orphan"> <key column="customer_id"></key> <one-to-many class="com.it.b_onetomany.Order"/> </set> </class> |
l Order.hbm.xml
<class name="com.hibernate.b_onetomany.Order" table="t_order"> <id name="xid"> <generator class="native"></generator> </id> <property name="price"></property>
<!-- 多對一:多個訂單屬於【一個客戶】 * name 肯定屬性名稱 * class 肯定自定義類型 * column 肯定從表的外鍵名稱 --> <many-to-one name="customer" class="com.hibernate.b_onetomany.Customer" column="customer_id"></many-to-one>
</class> |
@Test public void demo01(){ // 1 建立客戶,並保存客戶--成功 Session session = factory.openSession(); session.beginTransaction();
Customer customer = new Customer(); customer.setCname("田志成");
session.save(customer);
session.getTransaction().commit(); session.close(); } |
@Test public void demo02(){ // 2 建立訂單,保存訂單--成功,外鍵爲null Session session = factory.openSession(); session.beginTransaction();
Order order = new Order(); order.setPrice("998");
session.save(order);
session.getTransaction().commit(); session.close(); } |
@Test public void demo03(){ // 3 建立客戶和訂單,客戶關聯訂單,保存客戶? Session session = factory.openSession(); session.beginTransaction();
//1 客戶和訂單 Customer customer = new Customer(); customer.setCname("成成");
Order order = new Order(); order.setPrice("998");
//2 客戶關聯訂單 customer.getOrderSet().add(order);
//3 保存客戶 session.save(customer);
session.getTransaction().commit(); session.close(); }
|
@Test public void demo04(){ // 4 建立客戶和訂單,客戶關聯訂單,訂單也關聯客戶,保存客戶和訂單? // * 開發中優化程序 , n + 1 問題? // ** 解決方案1:客戶不關聯訂單 ,不建議 // ** 解決方案2:客戶放棄對訂單表外鍵值的維護。 // **** Customer.hbm.xml <set name="orderSet" inverse="true"> // ** inverse 將維護外鍵值的權利交予對象。至關於本身放棄。(反轉) Session session = factory.openSession(); session.beginTransaction();
//1 客戶和訂單 Customer customer = new Customer(); customer.setCname("成成");
Order order = new Order(); order.setPrice("998");
//2 客戶關聯訂單 customer.getOrderSet().add(order); //3 訂單也關聯客戶 order.setCustomer(customer);
//4 保存客戶 // * 1 save(order) -- insert --> 1,998 null // * 2 訂單管理客戶,此時null --預留update --> 更新全部(正常設置) // * 3 save(customer) -- insert --> 1,成成 // * 4 客戶關聯訂單 --> 預留update --> 更新訂單外鍵 (維護外鍵) // * 5 提交commit --> 執行2 和 4 session.save(order); session.save(customer);
session.getTransaction().commit(); session.close(); } |
l 在一對多開發中,一方通常都放棄對外鍵值的維護。及<set inverse="true
@Test public void demo032(){ // 32 建立客戶和訂單,客戶關聯訂單,保存客戶? --拋異常 // ** 解決方案2:級聯操做--級聯保存或更新 // ** Customer.hbm.xml <set cascade="save-update"> // ** 在保存客戶的同時,一併保存訂單 Session session = factory.openSession(); session.beginTransaction();
//1 客戶和訂單 Customer customer = new Customer(); //瞬時態 customer.setCname("成成");
Order order = new Order(); //瞬時態 order.setPrice("998");
//2 客戶關聯訂單 customer.getOrderSet().add(order);
//3 保存客戶 session.save(customer); //持久態 // 關聯操做都是持久態的,此時 持久態Customer 引用 一個 瞬時態的Order 拋異常
session.getTransaction().commit(); session.close(); } |
@Test public void demo05(){ // 5 查詢客戶,並刪除(持久態) // 默認:當刪除客戶,默認將訂單外鍵設置成null。 // 級聯刪除:刪除客戶時,並將客戶的訂單刪除。 // ** Customer.hbm.xml <set name="orderSet" cascade="delete"> Session session = factory.openSession(); session.beginTransaction();
Customer customer = (Customer) session.get(Customer.class, 10);
session.delete(customer);
session.getTransaction().commit(); session.close(); } |
l 一對多關係,存在父子關係。1表(主表)能夠成爲父表,多表(從表)也能夠子表。
總結:
主表不能刪除,從表已經引用(關聯)的數據
從表不能添加,主表不存在的數據。
@Test public void demo06(){ // 6 查詢客戶,並查詢訂單,解除客戶和訂單訂單的關係 // * 默認:客戶和訂單解除關係後,外鍵被設置成null,此時訂單就是孤兒。客戶和訂單都存在。 // * 孤兒刪除(孤子刪除),當訂單稱爲孤兒,一併刪除。客戶仍存在。 Session session = factory.openSession(); session.beginTransaction();
//1 查詢客戶 Customer customer = (Customer) session.get(Customer.class, 9);
//2查詢訂單 Order order = (Order) session.get(Order.class, 8);
//3 解除關係 customer.getOrderSet().remove(order);
session.getTransaction().commit(); session.close(); } |
save-update:A保存,同時保存B
delete:刪除A,同時刪除B,AB都不存在
delete-orphan:孤兒刪除,解除關係,同時將B刪除,A存在的。
若是須要配置多項,使用逗號分隔。<set cascade="save-update,delete">
all : save-update 和 delete 整合
all-delete-orphan : 三個整合
:
Student:
public class Student {
private Integer sid;
private String sname;
// 學生選擇多門課程.
private Set<Course> courses = new HashSet<Course>();
...
}
Course:
public class Course {
private Integer cid;
private String cname;
// 課程能夠被多個學生選擇:
private Set<Student> students = new HashSet<Student>();
...
}
Student.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.demo3.Student" table="student">
<id name="sid" column="sid">
<generator class="native"/>
</id>
<property name="sname" column="sname"/>
<!-- 配置多對多關聯關係 -->
<set name="courses" table="stu_cour">
<key column="sno"/>
<many-to-many class="cn.itcast. demo3.Course" column="cno"/>
</set>
</class>
</hibernate-mapping>
Course.hbm.xml
<hibernate-mapping>
<class name="cn.itcast. demo3.Course" table="course">
<id name="cid" column="cid">
<generator class="native"/>
</id>
<property name="cname" column="cname"/>
<!-- 配置多對多關聯關係映射 -->
<set name="students" table="stu_cour">
<key column="cno"/>
<many-to-many class="cn.itcast. demo3.Student" column="sno"/>
</set>
</class>
</hibernate-mapping>
l 當即檢索:當即查詢,在執行查詢語句時,當即查詢全部的數據。
l 延遲檢索:延遲查詢,在執行查詢語句以後,在須要時在查詢。(懶加載)
l 類級別檢索:當前的類的屬性獲取是否須要延遲。
l 關聯級別的檢索:當前類 關聯 另外一個類是否須要延遲。
l get:當即檢索。get方法一執行,當即查詢全部字段的數據。
l load:延遲檢索。默認狀況,load方法執行後,若是隻使用OID的值不進行查詢,若是要使用其餘屬性值將查詢 。 Customer.hbm.xml <class lazy="true | false">
lazy 默認值true,表示延遲檢索,若是設置false表示當即檢索。
@Test public void demo02() { //類級別 Session session = factory.openSession(); session.beginTransaction();
//1當即 // Customer customer = (Customer) session.get(Customer.class, 1); //2延遲 Customer customer = (Customer) session.load(Customer.class, 1);
//打印 System.out.println(customer.getCid()); System.out.println(customer.getCname());
session.getTransaction().commit(); session.close(); } |
l 容器<set> 提供兩個屬性:fetch、lazy
fetch:肯定使用sql格式
lazy:關聯對象是否延遲。
l fetch:join、select、subselect
join:底層使用迫切左外鏈接
select:使用多個select語句(默認值)
subselect:使用子查詢
l lazy:false、true、extra
false:當即
true:延遲(默認值)
extra:極其懶惰
l fetch="join" ,lazy無效。底層使用迫切左外鏈接,使用一條select將全部內容所有查詢。
@Test public void demo03() { //關聯級別:一對多, // * Customer.hbm.xml <set fetch="join"> // *** select語句使用左外鏈接,一次性查詢全部 Session session = factory.openSession(); session.beginTransaction();
//1 查詢客戶 Customer customer = (Customer) session.get(Customer.class, 1); System.out.println(customer.getCname());
//2 查詢客戶訂單數 Set<Order> orderSet = customer.getOrderSet(); System.out.println(orderSet.size());
//3 查詢客戶訂單詳情 for (Order order : orderSet) { System.out.println(order); }
session.getTransaction().commit(); session.close(); } |
l 當前對象 和 關聯對象 使用多條select語句查詢。
l lazy="false" , 當即,先查詢客戶select,當即查詢訂單select
l lazy="true",延遲,先查詢客戶select,須要訂單時,再查詢訂單select
l lazy="extra",極其懶惰(延遲),先查詢客戶select, 若是隻須要訂單數,使用聚合函數(不查詢詳情)
l 將使用子查詢。注意:必須使用Query不然看不到效果。
l lazy= 同上
@Test public void demo04() { //關聯級別:一對多, // 演示3:* Customer.hbm.xml <set fetch="subselect"> Session session = factory.openSession(); session.beginTransaction();
//1 查詢客戶 List<Customer> allCustomer = session.createQuery("from Customer").list(); Customer customer = allCustomer.get(0); System.out.println(customer.getCname());
//2 查詢客戶訂單數 Set<Order> orderSet = customer.getOrderSet(); System.out.println(orderSet.size());
//3 查詢客戶訂單詳情 for (Order order : orderSet) { System.out.println(order); }
session.getTransaction().commit(); session.close(); } |
l <many-to-one fetch="" lazy=""> (<one-to-one>)
l fetch取值:join、select
join:底層使用迫切左外鏈接
select:多條select語句
l lazy取值:false、proxy、no-proxy
false:當即
proxy:採用關聯對象 類級別檢索的策略。
訂單 關聯 客戶 (多對一)
訂單 當即得到 客戶,須要在客戶Customer.hbm.xml <class lazy="false">
訂單 延遲得到 客戶,須要在客戶Customer.hbm.xml <class lazy="true">
no-proxy 不研究
l fecth="join" select語句使用左外鏈接,此時lazy無效。
@Test public void demo05() { //關聯級別:多對一, // 演示1:* Order.hbm.xml <set fetch="join"> lazy無效 // * 注意:檢查Customer.hbm.xml 和 Order.hbm.xml 沒有額外的配置 Session session = factory.openSession(); session.beginTransaction();
//1 查詢訂單 Order order = (Order) session.get(Order.class, 1); System.out.println(order.getPrice());
//2 查詢訂單客戶信息 Customer customer = order.getCustomer(); System.out.println(customer.getCname());
session.getTransaction().commit(); session.close(); } |
l 將採用多條select語句,lazy="proxy"是否延遲,取決關聯對象 類級別檢索策略。
l lazy="false"
l lazy="proxy"
l 當客戶 關聯查詢 訂單,給每個客戶生產一個select語句查詢訂單。批量查詢使用in語句減小查詢訂單語句個數。
默認:select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)
l <set batch-size="5"> 5表示括號中?個數。
@Test public void demo06() { //批量查詢 Session session = factory.openSession(); session.beginTransaction();
//1 查詢全部客戶 List<Customer> allCustomer = session.createQuery("from Customer").list();
//2遍歷 for (Customer customer : allCustomer) { System.out.println(customer.getCname()); System.out.println(customer.getOrderSet().size()); }
session.getTransaction().commit(); session.close(); } |
檢索策略 |
優勢 |
缺點 |
優先考慮使用的場合 |
當即檢索 |
對應用程序徹底透明,無論對象處於持久化狀態仍是遊離狀態,應用程序均可以從一個對象導航到關聯的對象 |
(1)select語句多 (2)可能會加載應用程序不須要訪問的對象,浪費許多內存空間。 |
(1)類級別 (2)應用程序須要當即訪問的對象 (3)使用了二級緩存 |
延遲檢索 |
由應用程序決定須要加載哪些對象,能夠避免執行多餘的select語句,以及避免加載應用程序不須要訪問的對象。所以能提升檢索性能,並節省內存空間。 |
應用程序若是但願訪問遊離狀態的代理類實例,必須保證她在持久化狀態時已經被初始化。 |
(1)一對多或者多對多關聯 (2)應用程序不須要當即訪問或者根本不會訪問的對象
|
錶鏈接檢索 |
(1)對應用程序徹底透明,無論對象處於持久化狀態仍是遊離狀態,均可從一個對象導航到另外一個對象。 (2)使用了外鏈接,select語句少 |
(1)可能會加載應用程序不須要訪問的對象,浪費內存。 (2)複雜的數據庫錶鏈接也會影響檢索性能。 |
(1)多對一或一對一關聯 (2)須要當即訪問的對象 (3)數據庫有良好的錶鏈接性能。 |
Customer Get(int id)
Return Session.load(Customer.class,id);
1.經過OID檢索(查詢)
get()當即、若是沒有數據返回null
load()延遲,若是沒有數據拋異常。
2.導航對象圖檢索方式:關聯查詢
customer.getOrderSet()
user.getPost().getDepartment().getDepName();
3.原始sql語句
SQLQuery sqlQuery = session.createSQLQuery("sql 語句") --->表,表字段(列)
sqlQuery.list() 查詢全部
sqlQuery.uniqueResult() 查詢一個
4.HQL,hibernate query language hibernate 查詢語言【1】
Query query = session.createQuery("hql語句") --> 對象,對象屬性
5.QBC,query by criteria 純面對對象查詢語言【2】