2.Hibernate進階

一.持久化類的編寫規則

什麼是持久化類?
持久化:將內存中的數據永久存儲到關係型數據庫中的過程,Hibernate框架就是用來進行持久化的框架;
持久化類:一個Java對象與數據庫中的表建立了映射關係,那麼這類在Hibernate中稱爲是持久化類,可以通俗認爲,持久化類 = Java類 + 映射文件
持久化類的編寫規則:
對持久化類提供一個無參的構造方法:因爲Hibernate底層需要使用反射生成實例;
屬性需要私有,對私有屬性提供public的get和set方法:因爲Hibernate中需要獲取或者設置對象的值;
對持久化類提供一個唯一標識OID與數據庫主鍵對應:Java中通過對象的地址區分是否是同一個對象,數據庫中通過主鍵確定是否是同一個記錄,在Hibernate中通過持久化類的OID屬性區分是否是同一個對象。
持久化類中屬性儘量使用包裝類類型:因爲基本數據類型默認是0,0會有許多歧義,包裝類類型默認爲null,與0有着明顯的區別。
持久化類不要使用final修飾:延遲加載(也稱懶加載)本身是Hibernate的一個優化手段,返回的是一個代理的對象(javassist可以對沒有實現接口的類產生代理,其使用了非常底層的字節碼增強技術,繼承這個類進行代理)。這裏如果用final進行修飾,則不能被繼承了,也即不能產生代理對象,延遲加載也就失效了,則load方法和get方法也就一樣了。

二.主鍵生成策略

主鍵分類:
自然主鍵:主鍵的本身就是數據庫表中的一個字段(即爲實體中的一個具體的屬性)
舉例:創建一個人員表,人員都會有一個身份證號(唯一不可重複的),使用了身份證號作爲主鍵,這種主鍵稱爲是自然主鍵;
代理主鍵:主鍵的本身不是表中必須的一個字段(即不是實體中的某個具體的屬性)
舉例:創建一個人員表,沒有使用人員中的身份證號,用了一個與這個表不相關的字段,如ID,這種主鍵稱爲是代理主鍵;

:在實際開發中,儘量使用代理主鍵,因爲一旦自然主鍵參與到業務邏輯中,後期有可能需要修改源代碼;好的程序一般滿足OCP原則,即對程序的擴展是Open的,對源碼的修改是Close的。

Hibernate主鍵生成策略:
  在實際開發中,一般不允許用戶手動設置主鍵,一般將主鍵交給數據庫,手動編寫程序進行設置。在Hibernate中,爲了減少程序編寫,提供了很多種的主鍵生成策略:
increment:hibernate中提供的自動增長機制(沒有采用數據庫底層的自動增長),適用於short、int、long類型的主鍵,在單線程程序中使用;
說明:實現過程爲,首先發送一條語句“select max(id) from 表名”,然後讓id+1作爲下一條記錄的主鍵,這就可能產生線程安全問題。
identity:使用short、int、long類型的主鍵,使用的是數據庫底層的自動增長機制,適用於有自動增長機制功能的數據庫(MySQL、MSSQL),但是Oracle是沒有自動增長機制的;
sequence:使用short、int、long類型的主鍵,採用的是序列的方式。(Oracle支持序列,MySQL不支持序列);
uuid:適用於字符串類型的主鍵,使用hibernate中的隨機方式生成字符串主鍵;
native:本地策略,可以在identity和sequence之間進行自動切換;
assigned:hibernate放棄外鍵的管理,需要手動編寫程序或者用戶自己設置;
foreign:外部的,在一對一的一種關聯映射的情況下使用(瞭解即可)。

Hibernate主鍵生成策略實例:
創建項目:
在這裏插入圖片描述
引入jar包,創建兩個配置文件(hibernate.cfg.xml,Customer.hbm.xml),持久化類(Customer.java),工具類(HibernateUtils.java),測試類(hibernate_demo1.java)
代碼:
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 = "hibernate_start1.Customer" table = "cst_customer">
        <!-- 建立類中的屬性與表中的主鍵對應 -->
        <id name = "cust_id" column = "cust_id">
            <generator class="identity"/>
        </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"/>
    </class>
</hibernate-mapping>

hibernate.cfg.xml:

<?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_start1</property>
        <property name = "hibernate.connection.username" >root</property>
        <property name = "hibernate.connection.password" >2e5y8hxf</property>
        
        <!-- hibernate方言設置 -->
        <property name = "hibernate.dialect" >org.hibernate.dialect.MySQLDialect</property>

        
        <!-- 可選配置 -->
        <!-- 打印SQL -->
        <property name = "hibernate.show_sql">true</property>
        <!-- 格式化SQL -->
        <property name = "hibernate.format_sql">true</property>
        <!-- 自動創建表 -->
        <property name = "hibernate.hbm2ddl.auto">update</property>
        
        <mapping resource = "hibernate_start1/Customer.hbm.xml"/>
    </session-factory>    

</hibernate-configuration>

Customer.java

package hibernate_start1;

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;
	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;
	}
	@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 + "]";
	}
	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_phone() {
		return cust_phone;
	}
	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;
	}
    
}

HibernateUtils.java:

package hibernate_utils1;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

	public static final Configuration cfg;
	public static final SessionFactory sf;
	
	static{
		cfg = new Configuration().configure();
		sf = cfg.buildSessionFactory();
	}
	
	public static Session openSession(){
		return sf.openSession();
	}
}

hibernate_demo1.java:

package hibernate_start1;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import hibernate_utils1.HibernateUtils;

public class hibernate_demo1 {
    @Test
    public void demo1() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
    	Customer customer = new Customer();
    	customer.setCust_name("張無忌");
    	session.save(customer);
    	
    	transaction.commit();
    	session.close();
    	
    }
    
    @Test
    public void demo2() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
    	Customer customer = new Customer();
    	customer.setCust_name("趙敏");
    	session.save(customer);
    	
    	transaction.commit();
    	session.close();
    	
    }
}

測試結果:
分析:將Customer.hbm.xml文件中的主鍵生成策略參數<generator = “native”>改成increment,再將hibernate.cfg.xml文件中的自動建表方式改成create,這時創建的表是非自動增長的,此時再將create改成update,在測試類hibernate_demo1中同時測試demo1()和demo2(),當demo1()執行保存操作後,會查詢表中當前的最大的主鍵id:
在這裏插入圖片描述
提交操作前執行demo2(),到demo2()執行到提交操作前暫停,此時demo2()也會查詢表中的最大主鍵id:
在這裏插入圖片描述
也就是說demo1()和demo2()查詢到的主鍵id一樣。之後回去繼續執行demo1()中的提交,此時表中按照自動增長的方式添加了一條數據A(主鍵爲a+1),之後再執行demo2()中的提交操作,這時程序報錯,此時demo2()也將添加一條數據B(主鍵也爲a+1),所以兩個線程發生衝突。
解決方法:將increment換成identity,使用的是數據庫底層的自動增長機制,不受Hibernate約束。

三.持久化類的三種狀態

三種狀態概述:
1.瞬時態(transient):這種對象沒有持久化標識OID(相當於主鍵),沒有被session管理;
2.持久態(persistent):這種對象有持久化標識OID,並且被session管理;
3.脫管態(detached):這種對象有持久化標識OID,沒有別session管理;

    @Test
    public void demo1() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
    	Customer customer = new Customer();   //這裏customer爲瞬時態對象,沒有持久化標識OID,沒有被session管理
    	customer.setCust_name("張無忌");
    	session.save(customer);     //這行代碼執行過後,customer爲持久態對象,有持久化標識OID,並且被session管理
    	
    	transaction.commit();
    	session.close();
    	
    	System.out.println(customer.getCust_name());   //這裏的customer爲脫管態對象,有持久化標識OID,但是沒有被session管理
    	
    }

分析
Customer customer = new Customer(); //這裏customer爲瞬時態對象,沒有持久化標OID,沒有被session管理

session.save(customer); //這行代碼執行過後,customer爲持久態對象,有持久化標識OID,並且被session管理

System.out.println(customer.getCust_name()); //這裏的customer爲脫管態對象,有持久化標識OID,但是沒有被session管理

三種狀態轉換:
狀態轉換圖

在這裏插入圖片描述
瞬時態對象:
獲取瞬時態對象
  Customer customer = new Customer();
狀態轉換
瞬時態\to持久態:
  save(Object obj)、saveOrUpdate(Object obj)
瞬時態\to脫管態:
  customer.setCust_id(1l);

持久態對象:
獲取持久態對象
通過get()、load()、find()、iterate()對象獲取,比如:Customer customer = new Customer();
狀態轉換
持久態\to瞬時態:
  delete();
持久態\to脫管態:
close()、clear()、evict(Object obj);

脫管態對象:
獲取脫管態對象
  Customer customer = new Customer();
  customer.setCust_id(1l);
狀態轉換
脫管態\to持久態:update()、saveOrUpdate();
脫管態\to瞬時態:customer.setCust_id(null);

持久態對象特性:
主要特性:持久化類的持久化對象能夠自動地更新數據庫
實例:在獲取持久化對象後,再調用save等方法對數據庫進行更新

    @Test
    public void demo3() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
    	Customer customer = session.get(Customer.class, 1l);  //這裏不再需要調用save方法
    	customer.setCust_name("趙敏");  
	
    	transaction.commit();
    	session.close();
    	
    }

四.Hibernate一級緩存

一級緩存概述:
什麼是Hibernate一級緩存
Hibernate一級緩存就是指Session緩存,Session緩存是一塊內存空間,用來存放java對象。在使用Hibernate查詢對象的時候,首先會使用對象屬性的OID的值在Hibernate一級緩存中查找,如果找到匹配OID的值,則將該對象從一級緩存中取出(這裏可以認爲是複製)使用,不會再查詢數據庫;如果沒有找到相同OID值的對象,則會去數據庫中查找相應數據。當從數據庫中查詢到所需的數據時,該數據信息也會放置到一級緩存中。Hibernate一級緩存的作用就是減少對數據庫的訪問次數

Hibernate一級緩存的特點
  當程序調用Session接口的save()、update()、saveOrUpdate()時,如果一級緩存中沒有相應java對象,則將該對象信息加入到一級緩存中;
  當程序調用Session接口的load()、get(),以及Query接口的list()、iterator()方法時,會判斷一級緩存中是否存在該對象,有則從一級緩存中返回該對象,不會再查詢數據庫;如果一級緩存中沒有要查詢的對象,再到數據庫中查詢相應的對象,並將其添加到一級緩存中。
  當調用Session的close()方法時,Session緩存會被清空。

一級緩存實例

    @Test
    public void demo3() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
    	Customer customer1 = session.get(Customer.class, 1l);
    	System.out.println(customer1);
    	Customer customer2 = session.get(Customer.class, 1l);
    	System.out.println(customer2);   	
    	System.out.println(customer1 == customer2);
    	
    	transaction.commit();
    	session.close();
    	
    }

運行結果:
在這裏插入圖片描述
結果分析:
第一次查詢(customer1),一級緩存中沒有這個對象,所以需要去數據庫中查詢,所以打印了select…查詢語句,之後將這個對象存到一級緩存中;第二次查詢(customer2),一級緩存中已經有了這個對象,所以直接返回就行,不需要再去數據庫中查找,所以沒有打印select…查詢語句;而且最後兩次查詢得到的持續化對象地址比較爲true,說明說明這兩個對象存在同一個地方(即一級緩存中)。

快照區:
一級緩存包含:緩存區和快照區,底層實現一般是用map,緩存區和快照區分別對應於,map的鍵和值。
舉例
當執行代碼Customer customer = session.get(Customer.class, 1l); 時,會將緩存區中id爲1的數據拷貝一份到快照區,當執行customer.setCust_name(“趙敏”);時,會改變緩存區中的值(比如改爲“趙敏”),當進行事務提交時,會比較緩存區與快照區中id爲1的值是否一致,若一致,不更新數據庫,若不一致,則更新數據庫。

    	Customer customer = session.get(Customer.class, 1l);  
    	customer.setCust_name("趙敏");  
    	
        transaction.commit();

五.Hibernate中的事務管理

事務的隔離級別:
在這裏插入圖片描述
Hibernate中設置事務隔離級別:在覈心配置文件中設置,在Spring中默認爲4。

<property name = "hibernate.connection.isolation">4</property>

與線程綁定的session:
分析:在DAO層中執行的是對單個數據源的操作,在Service層中執行封裝的業務邏輯操作,在這裏s1()方法中的DAO1.aaa()和DAO2.bbb()必須保證連接對象是同一個Session對象,有兩種實現方式:
1.向下傳遞 DBUtils
2.使用ThreadLocal對象,即線程綁定對象(實現過程:如首先將DAO1.aaa()這個連接綁定到當前線程中,在調用DAO2.bbb()時,通過當前線程獲取Session連接,就能保住DAO1和DAO2連接的是同一個Session了)
在這裏插入圖片描述
Hibernate中:Hibernate框架內部已經綁定好了ThreadLocal,在SessionFactory提供了一個方法getCurrentSession()用於獲取當前線程中的Session。
實例
在工具類HibernateUtils中加入getCurrentSession()函數:
在這裏插入圖片描述
在測試類中,以Session session = HibernateUtils.getCurrentSession();方式獲取Session對象:
:由於這裏的Session是線程綁定的,隨着線程的結束而銷燬,所以不需要關閉。

    @Test
    public void demo4() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
    	Customer customer1 = session.get(Customer.class, 1l);
    	System.out.println(customer1);
    	Customer customer2 = session.get(Customer.class, 1l);
    	System.out.println(customer2);   	
    	System.out.println(customer1 == customer2);
    	
    	transaction.commit();
    	//session.close();
    	
    }

核心配置文件代碼:
在這裏插入圖片描述

五.Hibernate的其他API

1.Query
概述:Query代表Hibernate中面向對象的查詢操作。在Hibernate中,通常使用session.createQuery()方法接收一個HQL語句,然後調用Query的list()或uniqueResult()方法執行查詢。
HQL:Hibernate Query Language,其語法很想SQL,但HQL是完全面向對象的。

在Hibernate中使用Query對象的步驟:
(1)獲得Hibernate的Session對象;
(2)編寫HQL語句;
(3)調用session.createQuery創建查詢對象;
(4)如果HQL語句包含參數,則調用Query的setXxx語句來設置參數;
(5)調用Query的list()或者uniqueResult()方法來執行查詢;

舉例
查詢所有數據:

    @Test
    public void demo5() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
        String hql = "from Customer";
        Query query = session.createQuery(hql);
        List<Customer> list = query.list();
        System.out.println(list);
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
    	transaction.commit();
    	//session.close();  因爲這裏使用的是線程綁定的session,隨着線程的結束而關閉,不需要手動關閉   	
    }

查詢結果:
在這裏插入圖片描述
條件查詢:

    @Test
    public void demo5() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
        String hql = "from Customer where cust_name like ?";
        Query query = session.createQuery(hql);
        query.setParameter(0, "趙%");
        List<Customer> list = query.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
    	transaction.commit();	
    }

查詢結果:
在這裏插入圖片描述
分頁查詢:這裏分頁查詢查詢1頁,參數setFirstResult(0)代表從id爲0(不包括0)開始查詢,setMaxResults(2)這裏的2代表每頁有兩個記錄。

    @Test
    public void demo5() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//編寫代碼
        String hql = "from Customer";
        Query query = session.createQuery(hql);
        query.setFirstResult(0);
        query.setMaxResults(2);
        List<Customer> list = query.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
    	transaction.commit();	
    }

在這裏插入圖片描述

2.Query
概述:Criteria是一個完全面向對象,可擴展的條件查詢API,通過它完全不需要考慮數據庫底層是怎麼實現的,以及SQL語句是如何編寫的。Criteria查詢又稱QBC(Query by Criteria)查詢,是Hibernate中另一種對象檢索方式。

舉例:
查詢所有:

    @Test
    public void demo6() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//查詢所有
        Criteria criteria = session.createCriteria(Customer.class);
        List<Customer> list = criteria.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
        
    	transaction.commit();	
    }

條件查詢:

    @Test
    public void demo6() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//條件查詢
        Criteria criteria = session.createCriteria(Customer.class);
        criteria.add(Restrictions.like("cust_name", "趙%"));
        List<Customer> list = criteria.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	    
    	transaction.commit();	
    }

分頁查詢:

    @Test
    public void demo6() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//分頁查詢
        Criteria criteria = session.createCriteria(Customer.class);
        criteria.setFirstResult(0);
        criteria.setMaxResults(2);
        List<Customer> list = criteria.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	    
    	transaction.commit();	
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章