1 Hibernate工作原理
2 第一個Hibernate程序
-
舉例:primary
2.1 定義持久化對象(PO)
package com.eason.hibernate.po; public class Student { //這裏通常使用Integer而不是使用int,因爲低版本框架底層可能使用null與id進行比較,如果使用int類型,則會出現錯誤 private Integer id; private String name; private int age; private double score; public Student(String name, int age, double score) { super(); this.name = name; this.age = age; this.score = score; } public Student() { super(); // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + ", score=" + score + "]"; } }
2.2 配置映射文件
- Hibernate中主要涉及兩個配置文件:主配置文件和映射文件。這兩個配置文件的約束文件在Hibernate核心Jar文件 hibernate-core-5.0.1.Final.jar中的or.hibernate包中:
- 配置映射文件,即配置兩個關係:實體類與數據庫中表的映射關係,屬性與表中字段的映射關係。
- 在Student類所在的包中定義和配置student.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="com.eason.hibernate.po"> <class name="Student" table="t_student"> <id name="id" column="sid"> <generator class="native"></generator> </id> <property name="name" column="sname"></property> <property name="age" column="sage"></property> <property name="score" column="score"></property> </class> </hibernate-mapping>
2.3 配置主配置文件
2.3.1 配置DB連接四要素與方言
- 主配置文件中的數據庫連接相關屬性值可以在hibernate框架解壓目錄下的project/etc/hibernate.properties文件中找到。
- DB連接四要素與方言的key:
<!-- 連接四要素 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">02000059</property> <!-- 方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
2.3.2 配置數據源
- 數據源的key:
<!-- C3P0數據源 --> <property name="hibernate.connection.provider_class"> org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
- 注意:若不指定第三方數據源,將使用Hibernate內置的數據源
2.3.3 配置當前Session上下文
-
指定當前Session上下文爲線程,即在同一線程內所使用的Session爲同一Session。
<!-- 指定當前Session上下文爲線程 --> <property name="hibernate.current_session_context_class">thread</property>
2.3.4 配置自動建表
<!-- 自動建表 --> <property name="hibernate.hbm2ddl.auto">update</property>
2.3.5 配置控制檯SQL輸出
<!-- 控制檯SQL輸出 --> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property>
2.3.6 註冊映射文件
<!-- 註冊映射文件 --> <mapping resource="com/eason/hibernate/po/student.hbm.xml"/>
2.3.7 主配置文件整體配置情況
<?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://localhost:3306/test</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">02000059</property> <!-- 方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- C3P0數據源 --> <property name="hibernate.connection.provider_class"> org.hibernate.c3p0.internal.C3P0ConnectionProvider</property> <!-- 指定當前Session上下文爲線程 --> <property name="hibernate.current_session_context_class">thread</property> <!-- 自動建表 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 控制檯SQL輸出 --> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <!-- 註冊映射文件 --> <mapping resource="com/eason/hibernate/po/student.hbm.xml"/> </session-factory> </hibernate-configuration>
2.4 創建數據庫
-
使用Mysql自帶的test數據庫。
2.5 定義測試類
package com.eason.hibernate.test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import com.eason.hibernate.po.Student; public class Test { public static void main(String[] args) { //1.加載主配置文件和映射文件 Configuration configure = new Configuration().configure(); //2.創建session工廠對象 SessionFactory sessionFactory = configure.buildSessionFactory(); //3.開啓Session對象 Session session = sessionFactory.getCurrentSession(); try { //4.開啓事務 session.beginTransaction(); Student student = new Student("張三", 23, 93.5); //5.執行操作 session.save(student); //6.提交事務 session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); //7.回滾事務 session.getTransaction().rollback(); } } }
3 CRUD操作
3.1 工具類的創建
- 每次在獲取Session時,都需要通過Configuration對象加載主配置文件和映射文件,並且要創建SessionFactory對象,降低了開發和執行效率。所以,對於Session對象的獲取,可以創建一個工具類。
-
在primary項目中,創建一個utils頂層功能包,將工具類放入其中。
package com.eason.hibernate.hbnUtils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HbnUtils { private static SessionFactory sessionFactory; public static Session getSession() { return getSessionFactory().getCurrentSession(); } private static SessionFactory getSessionFactory() { if(sessionFactory == null || sessionFactory.isClosed()) { sessionFactory = new Configuration().configure().buildSessionFactory(); } return sessionFactory; } }
3.2 CURD測試
- 在primary項目的基礎上,再創建一個測試類MyTest。在其中通過使用工具類HbnUtils來測試增、刪、改和簡單查詢操作。
3.2.1 save()
//測試save() @org.junit.Test public void testSave() { Student student = new Student("李四", 24, 94.5); Session session = HbnUtils.getSession(); try { session.beginTransaction(); session.save(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.3.2 persist()
- save()爲Hibernate的API,而persist()爲JPA的API。均用於完成持久化。
//測試persist() @org.junit.Test public void testPersist() { Student student = new Student("李四", 24, 94.5); Session session = HbnUtils.getSession(); try { session.beginTransaction(); session.persist(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.2.3 delete()
- 其底層是根據id進行刪除的。所以,指定的刪除對象必須要有id屬性。
//測試delete() @org.junit.Test public void testDelete() { Student student = new Student(); student.setId(1); Session session = HbnUtils.getSession(); try { session.beginTransaction(); session.delete(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.2.4 update()
- 其底層是根據id進行修改的。所以,指定的修改對象必須要有id屬性。
//測試update() @org.junit.Test public void testUpdate() { Student student = new Student("趙六", 25, 95.5); student.setId(2); Session session = HbnUtils.getSession(); try { session.beginTransaction(); session.update(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.2 5 saveOrUpdate()
- 通過參數對象是否具有id值來判斷是執行save()操作還是執行update()操作。若參數對象具有id,但是該id在DB中不存在,則會拋出異常。
//測試saveOrUpdate() @org.junit.Test public void testSaveOrUpdate() { // 設置要插入或者修改爲的值 Student student = new Student("趙六", 28, 95.5); //設置要修改的對象的id。該語句決定是執行save還是update() student.setId(2); Session session = HbnUtils.getSession(); try { session.beginTransaction(); session.saveOrUpdate(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.2.6 get()
-
在查詢無果時,會給出null,但是不會拋出異常。
//測試get() @org.junit.Test public void testGet() { Session session = HbnUtils.getSession(); try { session.beginTransaction(); //沒有id爲90的student,其查詢結果爲null,沒有異常拋出 Student student = session.get(Student.class, 90); System.out.println(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.2.7 load()
-
在查詢無果時,會拋出異常。
//測//測試load() @org.junit.Test public void testLoad() { Session session = HbnUtils.getSession(); try { session.beginTransaction(); Student student = session.load(Student.class, 90); //沒有id爲90的student,拋出異常 System.out.println(student); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }
3.2.8 對於增、刪、改的底層SQL執行順序
- 對於不同操作對象的增、刪、改操作,無論其代碼的書寫順序是怎麼樣的,底層SQL的執行順序都是insert、update、delete。
- 若要修改其底層的執行順序,則可通過Session的flush()方法刷新Session完成。flush()會使得前後分隔爲兩部分,各部分會按照其順序依次執行。
4 詳解API
4.1 Configuration 接口
- org.hibernate.cfg.Configuration接口的作用是加載主配置文件以及映射文件,以實現對Hibernate的啓動。
- Configuration 實例的獲取方式:Configuration cfg = new Configuration().configure();。
4.1.1 new Configuration()
- new Configuration()會加載一個屬性文件hibernate.properties。該屬性文件中存放着數據連接配置、Hibernate配置等配置信息。一般情況下不用設置該屬性文件,其設置已經放在主配置文件中。若要設置,應將其放於src目錄中。該文件的模版存放於Hibernate框架解壓目錄下的project/etc中。
4.1.2 無參configure()方法
- configure()方法,默認用於加載和解析名稱爲hibernate.cfg.xml配置文件,並通過主配置文件找到並解析映射文件。該方法返回一個Configuration對象。所以,cfg是一個包含配置信息以及映射信息的Configuration對象。
- 在查看configure()方法的源碼:
4.1.3 帶參configure()方法
- Hibernate主配置文件默認名稱爲hibernate.cfg.xml,應存放在src類路徑下。但也可以更換路徑和文件名。此時,需要使用帶參的configure()方法。
4.2 SessionFactory接口
- org.hibernate.SessionFactory接口對象是由Configuration對象通過buildSessionFactory()方法創建的。創建該對象的目的是,用於開啓Session對象。
- SessionFactory sessionFactory = cfg.buildSessionFactory();。
4.2.1 SessionFactory對象特點
- 重量級對象(系統開銷大)、單例的、線程安全的。
- 按理說,單例對象一定是被共享的,是線程不安全的。但是查看SessionFactory接口的實現類SessionFactoryImpl源碼,可以看起大多數成員變量時final的,所以是線程安全的。
4.2.2 SessionFactory對象的使用原則
- 基於其是線程安全的重量級對象,其創建和銷燬時系統開銷大,又是單例的特點,SessionFactory對象一般不手工關閉,而是在應用結束時自動將其銷燬。因此,SessionFactory不用進行close()關閉。
4.3 Session接口
- org.hibernate.classic.Session接口是應用程序與Hibernate連接的核心API,是Hibernate嚮應用程序提供的操縱DB的最主要的接口。它提供了基本的保存、更新、刪除和查詢方法。由SessionFactory對象創建。
- Session s= sessionFactory.getCurrentSession();。
4.3.1 Session對象的特點
- 一個輕量級對象、線程不安全的、多例的。
- 在Web應用中,多個用戶對同一應用訪問,Hibernate會爲每個用戶創建一個Session對象。所以是多例的。Session中包含大量非final成員變量,對於同一個用戶的操作,可能會產生多個事務。這多個事務若同時對同一個Session的同一個成員變量進行訪問,就會引起併發問題。所以session是線程不安全的。
4.3.2 Session對象的使用原則
- 基於Session的以上特點,Session在使用時要做到一個線程一個Session,即一個事務一個Session。使用完畢,立即關閉。Session不要作爲某個類的成員變量出現,因爲這樣會出現多個實例對象對同一個session的共享,使其不安全。
4.3.3 Session對象的獲取
- SessionFactory對於Session對象的獲取,提供了兩種獲取方式:
- sessionFactory.openSession():創建一個新的 Session 對象。
- sessionFactory.getCurrentSession():獲取當前線程中的 Session 對象。
1、openSession()方式:每次執行一次openSession()方法,均會創建一個新的Session對象。
2、getCurrentSession()方式:每次獲取到的都是當前線程中的Session對象,都是同一個Session。
3、當前Session上下文:爲了保證一個線程一個Session,即一個線程中使用的Session是同一個對象,一般在獲取Session對象時,使用SessionFactory的getCurrentSession()方法。不過,使用該方法獲取Session對象,需要在主配置文件中對Session所處的上下文環境,即事務環境進行註冊。 - hibernate.current_session_context_class取值有三種:
取值 | 意義 |
---|---|
thread | 表示當前Session所處的環境爲本地事務環境,Session會與當前線程綁定 |
jta | 表示當前Session所處的環境爲分佈式事務環境 |
SpringSessionContext類 | SSH整合時使用。表示將當前Session的管理權交由Spring容器,而Spring容器中會有事務環境。即當前Session所處的環境爲Spring事務環境。 |
4、爲什麼getCurrentSession()方式獲取到的是同一個對象?
- getCurrentSession()首先會獲取到當前線程Thread.currentThread(),然後從ThreadLocal中讀取key爲當前線程的value。而該value即爲當前線程中的Session對象。
- ThreadLocal是用於在線程中共享數據的。其底層爲Map<Thread, Object>,即該map的key爲線程Thread,而value則爲要共享的數據。
- 當在Hibernate主配置文件中設置 hibernate.current_session_context_class屬性值爲thread時,就表明每個用戶獲取到的Session對象都是自己當前線程中的Session,是同一個Session。
5、Session的關閉:使用getCurrentSession()方法獲取的Session,在進行事務提交或者回滾後,會自動關閉,無需再手工進行close()。當然,也不能在最後的finally{}語句塊中對Session進行手工關閉。因爲,無論是執行事務的commit(),還是執行rollback(),均會在finally{}之前執行。也就是說,在執行finally{}之前,session已經關閉。若再手工關閉,將會拋出異常。
6、總結:兩種獲取Session對象方式的區別
- 獲取Session對象,可以使用openSession()方式,也可以使用getCurrentSession()方式。這兩種方式在本質上有着很大的區別。
getCurrentSession()方式 | openSession()方式 | |
---|---|---|
獲取的對象 | 無論執行多少次該方法,只要是在同一個線程中,獲取的都是同一個Session對象 | 每執行一次該方法,獲取到的都是一個新的Session對象 |
對象的關閉 | 自動關閉Session,無需手工關閉 | 必須手工關閉Session對象 |
環境的註冊 | 需要註冊Session的運行環境 | 無需註冊 |
查詢對事務的支持 | 查詢必須在事務內執行 | 查詢可以不在事務內執行 |
4.3.4 Session中的常用方法
- save()/persist:添加對象
- update:修改對象
- saveOrUpdate():添加或者修改對象
- delete():刪除對象
- get()/load():根據主鍵查詢
- createQuery()/createSQLQuery():創建查詢對象
- createCriteria():條件查詢,QBC,純面向對象語句。
4.4 Transaction接口
- 通過該接口,可以將事務從持久層,提升到業務層,由Session對象創建。
- 事務的開啓:session.beginTransaction();或者session.getTransaction().begin();
- 事務的提交:session.getTransaction().commit();
- 事務的回滾:session.getTransaction().rollback();
4.5 總結 -- 單例、多例和線程安全問題
- 單例和多例問題是指,當多個用戶訪問某個類時,系統是爲每個用戶創建一個該類實例,還是整個系統無論多少用戶訪問,只會創建一個該類實例。
- 線程安全問題是指,多個用戶同時在訪問同一個程序時,其對於某一個數據的修改,會不會影響到其他用戶中的該數據。若沒有影響,則是線程安全的;若有影響,則是線程不安全的。
- 現在對於HttpServlet、HttpSession、Struts2中的Action、Hibernate中的SessionFactory與Session,進行總結。
1、HttpServlet:
- 其是單例的。即無論多少用戶訪問同一個業務,如LoginServlet,Web容器只會創建一個該Servlet實例,而該實例是允許多用戶訪問的。
- 若Servlet中包含成員變量,則每個用戶對於成員變量的修改,均會影響到其他用戶所看到的該變量的值,所以這時線程是不安全的。若不包含成員變量,則是線程安全的。
2、HttpSession:
- 其是多例的。Web容器會爲每個用戶開闢一個Session,多個用戶會有多個Session,而每用戶只能訪問自己的Session。所以對於Session來說,就不存在併發訪問的情況,也就不存在線程安全問題。所以可以說是線程安全的。
3、Struts2的Action:
- 其是多例的。對於同一個業務,例如LoginAction,系統會爲每一個用戶創建一個LoginAction的實例,並使其成員變量username和password接收用戶提交的數據。同一用戶只能訪問自己的Action。所以,對於Aciton來說,就不存在併發訪問的情況,也就不存在線程安全的問題。所以可以說是線程安全的。
4、Hibernate的SessionFactory:
- 其是單例的。無論多少用戶訪問該項目,系統只會創建一個SessionFactory對象,即這個對象是可以被所有用戶訪問的。
- SessionFactory實現類中所包含的成員變量基本都是final常量,即任何用戶均不能修改。所以,也就不存在用戶的修改對其他用戶的影響問題,所以是線程安全的。
5、Hibernate的Session
- 其是多例的,系統會爲每個用戶創建一個Session。
- Session的實現類中定義了很多的非final成員變量,一個事務對成員變量所做的修改,會影響到另一個事務對同一個數據的訪問結果,所以線程是不安全的。
5 主配置文件詳解
- 主配置文件、主要配置三方面信息:
1、連接數據庫的基本信息:驅動、URL、用戶名、密碼。
2、Hibernate框架特性。
3、註冊映射文件,即指定映射文件的位置。 - 對於<session-factory/>標籤,可以包含多個<property>元素,用於配置hibernate與DB的連接信息以及數據源信息;可以包含多個<mapping>元素,用於註冊多個映射文件。
<session-factory> DB的連接信息 Hibernate特性 註冊映射文件 </session-factory>
5.1 數據庫連接設置
5.1.1 DB連接四要素的key
- 在數據庫連接四要素中的name屬性名稱,即連接四要素的key的寫法,connnection.與hibernate.connection.的效果是完全相同的。是爲了兼容以前的版本。
5.1.2 url取值的寫法
- 在數據庫連接的url屬性值的設置,一般寫法是jdbc:,mysql://localhost:3306/test。但是localhost:3306不寫也是正確的,即:jdbc:mysql:///test。
5.1.3 hibernate.properties文件
- 對於DB連接四要素,一般習慣於將其放入到屬性文件properties中,再將該屬性文件註冊到配置文件中。那麼,Hibernate主配置文件中的DB連接四要素是否需要放到屬性文件中呢?
- 通過Hibernate運行時所輸出的日誌信息可以看出,Hibernate在運行時默認會首先查找和加載一個屬性文件hibernate.properties。
- hibernate.cfg.xml文件的<properties/>標籤中設置的屬性幾乎都可以在這個文件中設置。其中就包括數據庫連接四要素。所以,在Hibernate中,無需專門設置讓hibernate.cfg.xml文件從屬性文件中讀取DB連接四要素。這個四要素可以直接放到hibernate.properties文件中,Hibernate即會自動讀取。
- 若要使用hibernate.properties文件,該文件是放在src的類路徑下的。當然,若程序中同時使用了兩個配置文件,且hibernate.properties與hibernate.cfg.xml中均對同一屬性進行了設置,那麼後者會將前者的值給覆蓋,即後者的優先級更高。(一般不使用hibernate.properties屬性文件)
5.2 方言設置
- 方言的設置信息可以從Hibernate核心Jar文件 hibernate-core-5.0.1.Final.jar中的or.hibernate.dialect 包中找到相應的類。複製全類名即爲方言的值。
5.3 自動建表設置
- hibernate.hbm2ddl.auto的取值有三種:create、create-drop、update。
1、create:每次加載主配置文件時都會刪除上一次生成的表,然後再生成新表,哪怕兩次表結構沒有任何變化。
2、create-drop:每次加載主配置文件時會生成表,但是sessionFactory一旦關閉,表就自動刪除。
3、update:當表字段增加時,會添加字段;當表字段減少時,不會減少字段。若表結構沒有變化,但是數據變化時,會修改數據。5.4 C3P0數據庫連接池配置
- Hibernate5默認使用的是其自己開發的內置(build-in)連接池。該連接池只是調試代碼時使用,在真正產品中不能夠使用。可以從控制檯的Hibernate啓動信息中查看到。
注意,若不指定第三方數據源,將使用Hibernate內置的數據源。該數據源無法自動將數據庫連接Connection釋放,而數據源作爲SessionFactory的屬性,一直被當前程序佔用,導致SessionFactory在程序運行結束後無法自動關閉。而這將導致整個程序無法結束。其表現爲Eclipse的Terminate按鈕一直顯示爲紅色運行狀態。 - 工業生產中,常用的數據源有DBCP、C3P0等。下面以C3P0爲例進行配置。
- 數據源在Hibernate主配置文件中的key,在Hibernate框架解壓目錄的project/etc下的hibernate.properties中可以找到:
- C3P0在主配置文件中的類名,在導入的包中可以查看到。
- 其在Hibernate主配置文件中的設置如下:
5.5 註冊映射文件
- 映射文件若存在多個,則可寫多個<mapping/>
6 映射文件詳解
- 配置映射文件,即配置兩個關係:
1、實體類與數據庫中表的映射關係。
2、屬性與表中字段的映射關係。6.1 <hibernate-mapping/>標籤
- 該標籤是Hibernate映射文件的根元素,其下可以包含多個<class/>標籤。常用的屬性主要是package屬性,用於指定其所包含的<class/>類所在的包。
6.2 <class/>標籤
- 該標籤用於設置PO類和數據表間的映射關係。
1、name屬性:指定持久化類。若<hibernate-mapping/>標籤設置了package屬性,name,此處的name屬性只需是類名即可;否則,需要是含包名的完整類名。
2、table屬性:指定與持久化類對應的數據表的名稱。若不指定,Hibernate將默認爲表名與類名相同。
3、catalog屬性:指定數據庫。默認爲主配置文件中指定的DB。6.3 <id/>與<property/>標籤
- 它們都是<class/>標籤的子標籤。常用有:
1、name屬性:指定持久化類的屬性名;
2、column屬性:指定數據表中的name屬性對應的字段名。若不指定,默認爲與name屬性同名;
3、length屬性:指定屬性所映射字段的長度,單位字節;
4、not-null屬性:爲指定字段添加非空約束;
5、unique屬性:爲指定字段添加唯一性約束;
6、type屬性:指定屬性所映射字段的類型。若省略Hibernate會自動從持久化類中檢測到類型。這裏類型取值支持兩類:java類型和Hibernate類型; - java類型指的是java代碼中的類型。若是基本數據類型,如int、double等,直接寫即可。如果是對象類型,則需要寫上全類名,如java.lang.String。
- Hibernate類型指的是Hibernate中定義的類型。在type中的雙引號中alt + ?可查看到Hibernate中的所有類型。
- 主要注意的是,若PO中屬性類型爲byte[], 則這裏的type應指定爲binary。
- 若PO中屬性類型爲boolean,type也可指定boolean,但是數據庫中的類型爲bit(二進制位)。用0代表false,1代表true。
7、sql-type屬性:當然映射文件中字段類型還支持一種類型,即數據庫中數據類型。但是這種類型的使用,需要使用<column>元素,其中有一個sql-type屬性用於指定字段類型。其值爲所使用DBMS的數據類型。
6.4 Hibernate常用的內置主鍵生成策略
- 主鍵生成策略,即主鍵的的生成方式,不同的生成策略,其生成主鍵的方法和值不同。注意,以下在測試時,需要將上一次測試時生成的表刪除,這樣才能夠測試出正確結果。
6.4.1 increment生成策略
- 該策略是Hibernate自己在維護主鍵的值。當準備在數據庫表中插入一條新記錄時,首先從數據表中獲取當前主鍵字段的最大值,然後在最大值基礎上加上1,作爲新輸入記錄的主鍵值,這就是increment生成策略。
- 用其生成的主鍵字段所對應的屬性類型可以是long、short、int以及其封裝類的類型。這種生成策略只有在沒有其他進程向同一張表中插入數據時才能夠使用,在高併發或者集羣環境下是不能夠使用的。
6.4.2 identity生成策略
- 該策略使用數據庫自身的自增長來維護主鍵值。如mysql使用auto_increment來維護。用其生成的主鍵字段所對應的屬性類型可以是long、short、int以及其封裝類的類型。
- 該策略在生成主鍵值時會出現以下情況:對於插入操作,即使最後的執行時回滾,DB中記錄主鍵值的變量也會增一。因爲該生成策略在發生回滾之前已經調用過DB的主鍵自增,所以無論是否提交,對於DB來說都已經執行。
6.4.3 sequence生成策略
- 在Oracle,DB2和PostgreSQL等數據庫中創建一個序列(sequence),然後Hibernate通過該序列爲當前記錄獲取主鍵值,從而爲實體對象賦予主鍵字段映射屬性值。此即sequence生成策略,用其生成的主鍵字段映射屬性值的類型可以是long、short、int以及其封裝類的類型。
- 對於MySql數據庫,原來是不支持序列的,但是稍微修改後,MySql也支持該生成策略。
- 其測試情況是,在第一次執行時,後臺會輸出以下查詢語句,並報錯。
- 該語句意思是,從hibernate-sequence序列表中查詢字段值next_val,該值將作爲要插入數據的主鍵值。當然,該查詢語句中的for update表示,對該表的操作是使用了樂觀鎖的。
- 打開數據庫,發現多生成了一張表hibernate_sequence,打開該表,發現其值爲空,無法進行自增運算。這就是報錯的原因:沒有初始值,手工爲其賦初值1即可進行運行。
- 再運行後,查看控制檯,其執行了三條SQL語句。第一條用於查詢出本次要使用的主鍵的值。第二條用於更新序列表中的值,爲下一次讀取做準備。第三條爲插入操作,使用本次讀取出來的值完成插入。
6.4.4 native生成策略
- 由Hibernate根據所使用的數據庫支持能力從identity、sequence生成策略中選擇一種。
- 使用這種標識符屬性生成策略可以根據不同的數據庫採用不同的生成策略,如Oracle中使用sequence,在MySQL中使用identity,便於Hibernate應用在不同的數據庫之間移植。
6.4.5 uuid生成策略
- uuid生成策略採用UUID(Universally Unique Identifier,通用唯一識別碼)算法來生成一個字符串類型的主鍵值,該值使用IP地址、JVM的啓動時間(精確到1/4秒)、當前系統時間和一個計數器值(在當前的JVM中唯一)經過計算產生,可以用於分佈式的Hibernate應用中。產生的標識符屬性是一個32位長度的字符串。使用這種生成策略,要求屬性的類型必須爲String類型。
- 這種標識符屬性生成策略生成的數值可以保證多個數據庫之間的唯一性,並且由於其生成與具體的數據庫沒有關係,所以其移植性較強。但是由於該值是32位長的字符串,所以佔用的數據庫空間較大,並且檢索速度較慢。不過,實際開發中使用這種生成策略較多。
- 除了使用Hibernate外,在JDBC中也可以使用uuid生成主鍵。因爲UUID是java.util包中的一個獨立的類。打開項目中的JRE System Library庫中的rt.jar,在其中找到java.util包,即可看到UUID這個類。
2.6.6 assigned生成策略
- 該生成策略的主鍵值來自於程序員的手工設置,即通過setId()方式設置。屬性類型可以是整型,也可以是String,但一般爲String。此生成策略,主要應用於業務相關主鍵。例如學號、×××號做主鍵。
7 持久對象狀態管理
7.1 Hibernate的對象狀態
- 對象的狀態一般是指對象的一組屬性的值。而這裏的狀態是指對象處於什麼存儲介質中。
- 用於存放對象的存儲介質有三個:普通內存(與Hibernate無關)、Session緩存、數據庫。對象處於不同的介質,就將處於不同的狀態。
1、瞬時態:transient狀態,對象在內存中存在,但是DB無記錄,與Session無關,是個過渡狀態;
2、持久態:persistent狀態,在內存中存在,DB中有記錄,與Session相關,在Session中有對象的副本;
3、遊離態:detached狀態,在內存中存在,在DB中有記錄,與Session無關;
4、無名態:在內存中不存在,但是在DB中有記錄,與Session無關。7.2 狀態轉換圖
7.3 狀態轉換常用方法
1、save():將瞬時態對象同步到DB中;
2、update():將遊離態對象同步到DB中;
3、delete():將指定的對象從session中刪除,同時也刪除DB中的該數據;
4、close():關閉Session對象;
5、clear():清空Session緩存;
6、saveOrUpdate():根據參數對象的id屬性是否爲null來判斷是執行保存還是更新操作;
7、evict():將指定對象僅僅從session中刪除,但不刪除DB中的該數據;
8、load()與get():將無名態對象轉換爲持久化對象;