hibernate 學習筆記 馬士兵

第1課 課程內容. 6

第2課Hibernate UML圖. 6

第3課 風格. 7

第4課 資源. 7

第5課 環境準備. 7

第6課 第一個示例HibernateHelloWorld 7

第7課 建立Annotation版本的HellWorld 9

第8課 什麼是O/RMapping 11

一、     定義:. 11

二、     Hibernate的創始人:. 11

三、     Hibernate做什麼:. 12

四、     Hibernate存在的原因:. 12

五、     Hibernate的優缺點:. 12

六、     Hibernate使用範圍:. 12

第9課Hibernate的重點學習:Hibernate的對象關係映射. 12

一、對象---關係映射模式. 12

二、常用的O/R映射框架:. 13

第10課 模擬Hibernate原理(OR模擬) 13

一、     項目名稱. 13

二、     原代碼. 13

第11課Hibernate基礎配置. 15

一、     提綱. 15

二、     介紹MYSQL的圖形化客戶端. 16

三、     Hibernate.cfg.xml:hbm2ddl.auto 16

四、     搭建日誌環境並配置顯示DDL語句. 16

五、     搭建Junit環境. 16

六、     ehibernate.cfg.xml : show_sql 17

七、     hibernate.cfg.xml :format_sql 17

八、     表名和類名不同,對錶名進行配置. 17

九、     字段名和屬性相同. 17

十、     字段名和屬性名不同. 17

十一、      不需要(持久化)psersistence的字段. 18

十二、      映射日期與時間類型,指定時間精度. 18

十三、      映射枚舉類型. 19

第12課 使用hibernate工具類將對象模型生成關係模型. 19

第13課ID主鍵生成策略. 20

一、     Xml方式. 20

<generator>元素(主鍵生成策略) 20

二、     annotateon方式. 21

1、AUTO默認. 21

2、IDENTITY 22

3、SEQUENCE 22

4、爲Oracle指定定義的Sequence 22

5、TABLE - 使用表保存id值. 23

三、     聯合主鍵. 24

1、xml方式. 24

2、annotation方式. 27

第14課Hibernate核心開發接口(重點) 29

一、     Configuration(AnnotationConfiguration) 29

二、     SessionFactory 29

三、     Session 29

1、    管理一個數據庫的任務單元. 29

2、    save(); 29

3、    delete() 29

4、    load() 29

5、    Get() 30

6、    load()與get()區別. 31

7、    update() 31

8、    saveOrUpdate() 32

9、    clear() 32

10、     flush() 33

11、     evict() 33

第15課 持久化對象的三種狀態. 35

一、     瞬時對象(TransientObject):. 35

二、     持久化對象(PersistentObject):. 35

三、     離線對象(DetachedObject):. 35

四、     三種狀態的區分:. 35

五、     總結:. 35

第16課 關係映射(重點) 36

一、     一對一 關聯映射. 36

(一)     唯一外鍵關聯-單向(unilateralism) 37

(二)     唯一外鍵關聯-雙向. 40

(三)     主鍵關聯-單向(不重要) 41

(四)     主鍵關聯-雙向(不重要) 44

(五)     聯合主鍵關聯(Annotation方式) 44

二、     component(組件)關聯映射. 45

(一)     Component關聯映射:. 45

(二)     User實體類:. 45

(三)     Contact值對象:. 46

(四)     xml--User映射文件(組件映射):. 46

(五)     annotateon註解. 46

(六)     導出數據庫輸出SQL語句:. 47

(七)     數據表結構:. 47

(八)     組件映射數據保存:. 47

三、     多對一– 單向. 48

(一)     對象模型圖:. 48

(二)     關係模型:. 48

(三)     關聯映射的本質:. 48

(四)     實體類. 48

(五)     xml方式:映射文件:. 49

(六)     annotation 50

(七)     多對一 存儲(先存儲group(對象持久化狀態後,再保存user)):. 50

(八)     重要屬性-cascade(級聯):. 51

(九)     多對一  加載數據. 51

四、     一對多- 單向. 51

(一)     對象模型:. 52

(二)     關係模型:. 52

(三)     多對一、一對多的區別:. 52

(四)     實體類. 52

(五)     xml方式:映射. 52

(六)     annotateon註解. 53

(七)     導出至數據庫(hbmàddl)生成的SQL語句:. 53

(八)     一對多 單向存儲實例:. 53

(九)     生成的SQL語句:. 54

(十)     一對多,在一的一端維護關係的缺點:. 54

(十一)    一對多 單向數據加載:. 54

(十二)    加載生成SQL語句:. 54

五、     一對多- 雙向. 54

(一)     xml方式:映射. 55

(二)     annotateon方式註解. 55

(三)     數據保存:. 56

(四)     關於inverse屬性:. 56

(五)     Inverse和cascade區別:. 56

(六)     一對多雙向關聯映射總結:. 57

六、     多對多- 單向. 57

(一)     實例場景:. 57

(二)     對象模型:. 57

(三)     關係模型:. 57

(四)     實體類. 57

(五)     xml方式:映射. 58

(六)     annotation註解方式. 58

(七)     生成SQL語句. 59

(八)     數據庫表及結構:. 59

(九)     多對多關聯映射 單向數據存儲:. 59

(十)     多對多關聯映射 單向數據加載:. 61

七、     多對多- 雙向. 61

(一)     xml方式:映射. 61

(二)     annotation註解方式. 62

八、     關聯關係中的CRUD_Cascade_Fetch 63

九、     集合映射. 63

十、     繼承關聯映射. 64

(一)     繼承關聯映射的分類:. 64

(二)     對象模型:. 64

(三)     單表繼承SINGLE_TABLE:. 64

(四)     具體表繼承JOINED:. 70

(五)     類表繼承TABLE_PER_CLASS 72

(六)     三種繼承關聯映射的區別:. 74

第17課hibernate樹形結構(重點) 75

一、     節點實體類:. 75

二、     xml方式:映射文件:. 75

三、     annotation註解. 76

四、     測試代碼:. 76

五、     相應的類代碼:. 76

第18課 作業-學生、課程、分數的映射關係. 79

一、     設計. 79

二、     代碼:. 79

三、     注意. 80

第19課Hibernate查詢(Query Language) 80

一、     Hibernate可以使用的查詢語言. 80

二、     實例一. 80

三、     實體一測試代碼:. 82

四、     實例二. 86

五、     實例二測試代碼. 87

第20課Query by Criteria(QBC) 89

一、     實體代碼:. 89

二、     Restrictions用法. 90

三、     工具類Order提供設置排序方式. 91

四、     工具類Projections提供對查詢結果進行統計與分組操作. 91

五、     QBC分頁查詢. 92

六、     QBC複合查詢. 92

七、     QBC離線查詢. 92

第21課Query By Example(QBE) 92

一、     實例代碼. 92

第22課Query.list與query.iterate(不太重要) 93

一、     query.iterate查詢數據. 93

二、     query.list()和query.iterate()的區別. 94

三、     兩次query.list() 94

第23課 性能優化策略. 95

第24課hibernate緩存. 95

一、     Session級緩存(一級緩存) 95

二、     二級緩存. 95

1、    二級緩存的配置和使用:. 96

2、    二級緩存的開啓:. 96

3、    指定二級緩存產品提供商:. 96

4、    使用二級緩存. 97

5、    應用範圍. 99

6、    二級緩存的管理:. 99

7、    二級緩存的交互. 100

8、    總結. 102

三、     查詢緩存. 102

四、     緩存算法. 103

第25課 事務併發處理. 104

一、     數據庫的隔離級別:併發性作用。. 104

1、    Mysql查看數據庫隔離級別:. 104

2、    Mysql數據庫修改隔離級別:. 104

二、     事務概念(ACID) 104

三、     事務併發時可能出現問題. 104

第26課hibernate悲觀鎖、樂觀鎖. 105

一、     悲觀鎖. 105

1、    悲觀鎖的實現. 105

2、    悲觀鎖的適用場景:. 105

3、    實例:. 105

4、    悲觀鎖的使用. 106

5、    執行輸出SQL語句:. 106

二、     樂觀鎖. 107


第1課 課程內容

1、  HelloWorld

a)        Xml

b)        Annotction

2、  Hibernate原理模擬-什麼是O/RMapping以及爲什麼要有O/RMapping

3、  常風的O/R框架

4、  Hibernate基礎配置

5、  Hibernate核心接口介紹

6、  對象的三種狀態

7、  ID生成策略

8、  關係映射

9、  Hibernate查詢(HQL)

10、    在Struts基礎上繼續完美BBS2009

11、    性能優化

12、    補充話題


第2課 Hibernate UML圖

第3課 風格

1、  先脈絡,後細節

2、  先操作、後原理

3、  重Annotation,輕xml配置文件

a)        JPA (可以認爲EJB3的一部分)

b)        Hibernate– extension

 

第4課 資源

1、  http://www.hibernate.org

a)        hibernate-distribution-3.3.2.GA-dist.zip

b)        hibernate-annotations-3.4.0.GA.zip

c)         slf4j-1.5.10.zip    (hibernate內部日誌用)

2、  hibernatezh_CN文檔

3、  hibernateannotateon references

 

第5課 環境準備

1、  下載hibernate3.3.2

2、  下載hibernate-annotations-3.4.0

3、  注意閱讀hibernate compatibility matrix

4、  下載slf4j 1.5.8

 

第6課 第一個示例Hibernate HelloWorld

1、  建立新的java項目,名爲hibernate_0100_HelloWorld

2、  學習建立User-liberary-hibernate,並加入相應的jar包

a)        項目右鍵-build path-configure build path-add library

b)        選擇User-library ,在其中新建library,命名爲hibernate

c)         在該library中加入hibernate所需的jar名

                        i.             Hibernatecore

                       ii.             /lib/required

                     iii.             Slf-nopjar

3、  引入mysql的JDBC驅動名

4、  在mysql中建立對應的數據庫以及表

a)        Create databasehibernate;

b)        Usehibernate;

c)         Createtable Student (id int primary key, name varchar(20),age int);

5、  建立hibernate配置文件hibernate.cfg.xml

a)        從參考文檔中copy

b)        修改對應的數據庫連接

c)         註釋提暫時不需要的內容

6、  建立Student類

7、  建立Student映射文件Student.hbm.xml

a)        參考文檔

8、  將映射文件加入到hibernate.cfg.xml

a)        參考文檔

9、  寫測試類Main,在Main中對Student對象進行直接的存儲測試


  1. public static void main(String[] args) {  
  2.           
  3.         Configuration cfg = null;  
  4.         SessionFactory sf = null;  
  5.         Session session = null;  
  6.           
  7.         Student s = new Student();  
  8.         s.setId(2);  
  9.         s.setName("s1");  
  10.         s.setAge(1);  
  11.           
  12.         /* 
  13.          * org.hibernate.cfg.Configuration類的作用: 
  14.          * 讀取hibernate配置文件(hibernate.cfg.xml或hiberante.properties)的. 
  15.          * new Configuration()默認是讀取hibernate.properties 
  16.          * 所以使用new Configuration().configure();來讀取hibernate.cfg.xml配置文件 
  17.          */  
  18.         cfg = new Configuration().configure();  
  19.           
  20.         /* 
  21.          * 創建SessionFactory 
  22.          * 一個數據庫對應一個SessionFactory  
  23.          * SessionFactory是線線程安全的。 
  24.          */  
  25.         sf = cfg.buildSessionFactory();  
  26.           
  27.         try {  
  28.             //創建session  
  29.             //此處的session並不是web中的session  
  30.             //session只有在用時,才建立concation,session還管理緩存。  
  31.             //session用完後,必須關閉。  
  32.             //session是非線程安全,一般是一個請求一個session.  
  33.             session = sf.openSession();  
  34.               
  35.             //手動開啓事務(可以在hibernate.cfg.xml配置文件中配置自動開啓事務)  
  36.             session.beginTransaction();  
  37.             /* 
  38.              * 保存數據,此處的數據是保存對象,這就是hibernate操作對象的好處, 
  39.              * 我們不用寫那麼多的JDBC代碼,只要利用session操作對象,至於hibernat如何存在對象,這不需要我們去關心它, 
  40.              * 這些都有hibernate來完成。我們只要將對象創建完後,交給hibernate就可以了。 
  41.              */  
  42.             session.save(s);  
  43.             session.getTransaction().commit();  
  44.         } catch (HibernateException e) {  
  45.             e.printStackTrace();  
  46.             //回滾事務  
  47.             session.getTransaction().rollback();  
  48.         } finally {  
  49.             //關閉session  
  50.             session.close();  
  51.             sf.close();  
  52.         }     
  53.     }  

 

1、  FAQ:

a)        要調用newConfiguration().configure().buildSessionFactory(),而不是省略

*       org.hibernate.cfg.Configuration類的作用:

         *讀取hibernate配置文件(hibernate.cfg.xml或hiberante.properties)的.

         *new Configuration()默認是讀取hibernate.properties

         * 所以使用new Configuration().configure();來讀取hibernate.cfg.xml配置文件

 

注意:在hibernate裏的操作都應該放在事務裏

 

第7課 建立Annotation版本的HellWorld

         注意:要求hibernate3.0版本以後支持

1、  創建teacher表,create table teacher(id int primary key,namevarchar(20),title varchar(10));

2、  創建Teacher類

  1. public class Teacher {  
  2.     private int id;   
  3.     private String name;      
  4.     private String title;  
  5.     //設置主鍵使用@Id  
  6.     public int getId() {  
  7.         return id;  
  8.     }  
  9.     public void setId(int id) {  
  10.         this.id = id;  
  11.     }  
  12.     public String getName() {  
  13.         return name;  
  14.     }  
  15.     public void setName(String name) {  
  16.         this.name = name;  
  17.     }  
  18.     public String getTitle() {  
  19.         return title;  
  20.     }  
  21.     public void setTitle(String title) {  
  22.         this.title = title;  
  23.     }  
  24. }  

1、  在hibernate library中加入annotation的jar包

a)        Hibernateannotations jar

b)        Ejb3persistence jar

c)         Hibernatecommon annotations jar

d)        注意文檔中沒有提到hibernate-common-annotations.jar文件

2、  參考Annotation文檔建立對應的註解

  1. import javax.persistence.Entity;  
  2. import javax.persistence.Id;  
  3. /** @Entity 表示下面的這個Teacher是一個實體類 
  4.  * @Id 表示主鍵Id*/  
  5. @Entity //***  
  6. public class Teacher {  
  7.     private int id;   
  8.     private String name;      
  9.     private String title;  
  10.     //設置主鍵使用@Id  
  11.     @Id  //***  
  12.     public int getId() {  
  13.         return id;  
  14.     }  
  15.     public void setId(int id) {  
  16.         this.id = id;  
  17.     }  
  18.     public String getName() {  
  19.         return name;  
  20.     }  
  21.     public void setName(String name) {  
  22.         this.name = name;  
  23.     }  
  24.     public String getTitle() {  
  25.         return title;  
  26.     }  
  27.     public void setTitle(String title) {  
  28.         this.title = title;  
  29.     }}  

1、  在hibernate.cfg.xml中建立映射<maping class=…/>

<mapping class="com.wjt276.hibernate.model.Teacher"/>

注意:<mapping>標籤中使用的是class屬性,而不是resource屬性,並且使用小數點(.)導航,而不是”/”

2、  參考文檔進行測試


  1. public static void main(String[] args) {          
  2.         Teacher t = new Teacher();  
  3.         t.setId(1);  
  4.         t.setName("s1");  
  5.         t.setTitle("中級");  
  6.         //注此處並不是使用org.hibernate.cfg.Configuration來創建Configuration  
  7. //而使用org.hibernate.cfg.AnnotationConfiguration來創建Configuration,這樣就可以使用Annotation功能  
  8.         Configuration cfg = new AnnotationConfiguration();  
  9.           
  10.         SessionFactory sf = cfg.configure().buildSessionFactory();  
  11.         Session session = sf.openSession();  
  12.           
  13.         session.beginTransaction();  
  14.         session.save(t);  
  15.         session.getTransaction().commit();  
  16.           
  17.         session.close();  
  18.         sf.close();  
  19.     }  

1、  FAQ:@ 後不給提示

解決方法:windows→Proferences→seach “Content Assist”設置Auto-Activation如下:



——————————————————————————————————————————————————————————————————————————————————————————————

第8課 什麼是O/R Mapping

一、             定義:

ORM(ObjectRelational Mapping)---是一種爲了解決面向對象與關係型數據庫存在的互不匹配的現象的技術。簡單說:ORM是通過使用描述對象和數據庫之間映射的元數據,將Java程序中的對象自動持久化到關係數據中。本質上就是將數據從一種形式轉換到另外一種形式。


分層後,上層不需要知道下層是如何做了。

分層後,不可以循環依賴,一般是單向依賴。

 

一、             Hibernate的創始人:

Gavin King

 

二、             Hibernate做什麼:

1、    就是將對象模型(實體類)的東西存入關係模型中,

2、    實體中類對應關係型庫中的一個表,

3、    實體類中的一個屬性會對應關係型數據庫表中的一個列

4、    實體類的一個實例會對應關係型數據庫表中的一條記錄。

%%將對象數據保存到數據庫、將數據庫數據讀入到對象中%%

 

OOA---面向對象的分析、面向對象的設計

OOD---設計對象化

OOP---面向對象的開發

阻抗不匹配---例JAVA類中有繼承關係,但關係型數據庫中不存在這個概念這就是阻抗不匹配。Hibernate可以解決這個問題

 

三、             Hibernate存在的原因:

1、  解決阻抗不匹配的問題;

2、  目前不存在完整的面向對象的數據庫(目前都是關係型數據庫);

3、  JDBC操作數據庫很繁瑣

4、  SQL語句編寫並不是面向對象

5、  可以在對象和關係表之間建立關聯來簡化編程

6、  O/RMapping簡化編程

7、  O/RMapping跨越數據庫平臺

8、  hibernate_0200_OR_Mapping_Simulation

 

四、             Hibernate的優缺點:

1、  不需要編寫的SQL語句(不需要編輯JDBC),只需要操作相應的對象就可以了,就可以能夠存儲、更新、刪除、加載對象,可以提高生產效;

2、  因爲使用Hibernate只需要操作對象就可以了,所以我們的開發更對象化了;

3、  使用Hibernate,移植性好(只要使用Hibernate標準開發,更換數據庫時,只需要配置相應的配置文件就可以了,不需要做其它任務的操作);

4、  Hibernate實現了透明持久化:當保存一個對象時,這個對象不需要繼承Hibernate中的任何類、實現任何接口,只是個純粹的單純對象—稱爲POJO對象(最純粹的對象—這個對象沒有繼承第三方框架的任何類和實現它的任何接口)

5、  Hibernate是一個沒有侵入性的框架,沒有侵入性的框架我們一般稱爲輕量級框架

6、  Hibernate代碼測試方便。

 

五、             Hibernate使用範圍:

1.       針對某一個對象,簡單的將它加載、編輯、修改,且修改只是對單個對象(而不是批量的進行修改),這種情況比較適用;

2.       對象之間有着很清晰的關係(例:多個用戶屬於一個組(多對一)、一個組有多個用戶(一對多));

3.       聚集性操作:批量性添加、修改時,不適合使用Hibernate(O/映射框架都不適合使用);

4.       要求使用數據庫中特定的功能時不適合使用,因爲Hibernate不使用SQL語句;

 

第9課 Hibernate的重點學習:Hibernate的對象關係映射

一、對象---關係映射模式

l         屬性映射;

l         類映射:

l         關聯映射:

n         一對一;

n         一對多;

n         多對多。

 

二、常用的O/R映射框架:

1、  Hibernate

2、  ApacheOJB

3、  JDO(是SUN提出的一套標準—Java數據對象)

4、  Toplink(Orocle公司的)

5、  EJB(2.0X中有CMP;3.0X提出了一套“Java持久化API”---JPA)

6、  IBatis(非常的輕量級,對JDBC做了一個非常非常輕量級的包裝,嚴格說不是O/R映射框架,而是基於SQL的映射(提供了一套配置文件,把SQL語句配置到文件中,再配置一個對象進去,只要訪問配置文件時,就可得到對象))

7、  JAP(是SUN公司的一套標準)

a)        意願統一天下

 

第10課 模擬Hibernate原理(OR模擬)

         我們使用一個項目來完成

         功能:有一個配置文件,文件中完成表名與類名對象,字段與類屬性對應起來。

測試驅動開發

 

一、                      項目名稱

         hibernate_0200_OR_Mapping_Simulation

二、             原代碼

  1. Test類:  
  2.     public static void main(String[] args) throws Exception{  
  3.           
  4.         Student s = new Student();  
  5.         s.setId(10);  
  6.         s.setName("s1");  
  7.         s.setAge(1);  
  8.           
  9.         Session session = new Session();//此Session是我們自己定義的Session  
  10.           
  11.         session.save(s);  
  12. }  

  1. Session類  
  2. import java.lang.reflect.Method;  
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.PreparedStatement;  
  6. import java.util.HashMap;  
  7. import java.util.Map;  
  8. import com.wjt276.hibernate.model.Student;  
  9. public class Session {  
  10.     String tableName = "_Student";  
  11.     Map<String,String> cfs = new HashMap<String,String>();  
  12.     String[] methodNames;//用於存入實體類中的get方法數組  
  13.     public Session(){  
  14.         cfs.put("_id""id");  
  15.         cfs.put("_name""name");  
  16.         cfs.put("_age""age");  
  17.         methodNames = new String[cfs.size()];  
  18.     }  
  19.     public void save(Student s) throws Exception{  
  20.         String sql = createSQL();//創建SQL串  
  21.         Class.forName("com.mysql.jdbc.Driver");  
  22.         Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hibernate","root","root");  
  23.         PreparedStatement ps = conn.prepareStatement(sql);  
  24.         //  
  25.         for(int i = 0; i < methodNames.length; i++){  
  26.             Method m = s.getClass().getMethod(methodNames[i]);//返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法  
  27.             Class r = m.getReturnType();//返回一個 Class 對象,該對象描述了此 Method 對象所表示的方法的正式返回類型  
  28.             if(r.getName().equals("java.lang.String")) {  
  29.                 //對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。  
  30.                 //個別參數被自動解包,以便與基本形參相匹配,基本參數和引用參數都隨需服從方法調用轉換  
  31.                 String returnValue = (String)m.invoke(s);  
  32.                 ps.setString(i + 1, returnValue);  
  33.             }  
  34.             if(r.getName().equals("int")) {  
  35.                 Integer returnValue = (Integer)m.invoke(s);  
  36.                 ps.setInt(i + 1, returnValue);  
  37.             }  
  38.             if(r.getName().equals("java.lang.String")) {  
  39.                 String returnValue = (String)m.invoke(s);  
  40.                 ps.setString(i + 1, returnValue);  
  41.             }  
  42.             System.out.println(m.getName() + "|" + r.getName());  
  43.         }         
  44.         ps.executeUpdate();  
  45.         ps.close();  
  46.         conn.close();  
  47.     }  
  48.     private String createSQL() {  
  49.         String str1 = "";  
  50.         int index = 0;  
  51.           
  52.         for(String s : cfs.keySet()){  
  53.             String v = cfs.get(s);//取出實體類成員屬性  
  54.             v = Character.toUpperCase(v.charAt(0)) + v.substring(1);//將成員屬性第一個字符大寫  
  55.             methodNames[index] = "get" + v;//拼實體類成員屬性的getter方法  
  56.             str1 += s + ",";//根據表中字段名拼成字段串  
  57.             index ++;  
  58.         }  
  59.         str1 = str1.substring(0,str1.length() -1);  
  60.         String str2 = "";  
  61.         //根據表中字段數,拼成?串  
  62.         for (int i = 0; i < cfs.size(); i++){    str2 += "?,";}  
  63.         str2 = str2.substring(0,str2.length() -1);  
  64.         String sql = "insert into " + tableName + "(" + str1 + ")" + " values (" + str2 + ")";  
  65.         System.out.println(sql);  
  66.         return sql;  
  67.     }}  

第11課 Hibernate基礎配置

一、 提綱

1、  對應項目:hibernate_0300_BasicConfiguration

2、  介紹MYSQL的圖形化客戶端

3、  Hibernate.cfg.xml:hbm2ddl.auto

a) 先建表還是先建實體類

4、  搭建日誌環境並配置顯示DDL語句

5、  搭建Junit環境

a) 需要注意Junit的Bug

6、  ehibernate.cfg.xml: show_sql

7、  hibernate.cfg.xml:format_sql

8、  表名和類名不同,對錶名進行配置

a) Annotation:@Table

b) Xml:自己查詢

9、  字段名和屬性相同

a) 默認爲@Basic

b) Xml中不用寫column

10、    字段名和屬性名不同

a) Annotation:@Column

b) Xml:自己查詢

11、    不需要psersistence的字段

a) Annotation:@Transient

b) Xml:不寫

12、    映射日期與時間類型,指定時間精度

a) Annotation:@Temporal

b) Xml:指定type

13、    映射枚舉類型

a) Annotation:@Enumerated

b) Xml:麻煩

14、    字段映射的位置(field或者get方法)

a) Best practice:保持field和get/set方法的一致

15、    @Lob

16、    課外:CLOB BLOB類型的數據存取

17、    課外:Hibernate自定義數據類型

18、    Hibernate類型

 

二、 介紹MYSQL的圖形化客戶端

這樣的軟件網絡很多,主要自己動手做

三、 Hibernate.cfg.xml:hbm2ddl.auto

在SessionFactory創建時,自動檢查數據庫結構,或者將數據庫schema的DDL導出到數據庫. 使用 create-drop時,在顯式關閉SessionFactory時,將drop掉數據庫schema.取值validate | update | create | create-drop

四、 搭建日誌環境並配置顯示DDL語句

我們使用slf接口,然後使用log4j的實現。

1、  首先引入log4j的jar包(log4j-1.2.14.jar),

2、  然後再引入slf4j實現LOG4J和適配器jar包(slf4j-log4j12-1.5.8.jar)

3、  最後創建log4j的配置文件(log4j.properties),並加以修改,只要保留

log4j.logger.org.hibernate.tool.hbm2ddl=debug

五、 搭建Junit環境

1、首先引入Junit 類庫 jar包 (junit-4.8.1.jar)

2、在項目名上右鍵→new→Source Folder→輸入名稱→finish

3、注意,你對哪個包進行測試,你就在測試下建立和那個包相同的包

4、建立測試類,需要在測試的方法前面加入”@Test”


  1. public class TeacherTest {  
  2.       
  3.     private static SessionFactory sf = null;  
  4.       
  5.     @BeforeClass//表示Junit此類被加載到內存中就執行這個方法  
  6.     public static void beforClass(){  
  7.         sf = new AnnotationConfiguration().configure().buildSessionFactory();  
  8.     }  
  9.     @Test//表示下面的方法是測試用的。  
  10.     public void testTeacherSave(){  
  11.         Teacher t = new Teacher();  
  12.         t.setId(6);  
  13.         t.setName("s1");  
  14.         t.setTitle("中級");  
  15.         Session session = sf.openSession();  
  16.         session.beginTransaction();  
  17.         session.save(t);  
  18.         session.getTransaction().commit();  
  19.           
  20.         session.close();  
  21.     }  
  22.       
  23.     @AfterClass//Junit在類結果時,自動關閉  
  24.     public static void afterClass(){  
  25.         sf.close();  
  26.     }}  

六、 ehibernate.cfg.xml : show_sql

輸出所有SQL語句到控制檯. 有一個另外的選擇是把org.hibernate.SQL這個log category設爲debug。

取值: true | false

七、 hibernate.cfg.xml :format_sql

在log和console中打印出更漂亮的SQL。


取值: true | false

True樣式:

  1. 16:32:39,750 DEBUG SchemaExport:377 -   
  2.     create table Teacher (  
  3.         id integer not null,  
  4.         name varchar(255),  
  5.         title varchar(255),  
  6.         primary key (id)  
  7.  )  

False樣式:

  1. 16:33:40,484 DEBUG SchemaExport:377 - create table Teacher (id integer not null, name varchar(255), title varchar(255), primary key (id))  

八、 表名和類名不同,對錶名進行配置


Annotation:使用 @Table(name=”tableName”) 進行註解

         例如:

  1.     /** 
  2.  * @Entity 表示下面的這個Teacher是一個實體類 
  3.  * @Table 表示映射到數據表中的表名,其中的name參數表示"表名稱" 
  4.  * @Id 表示主鍵Id,一般放在getXXX前面 
  5.  */  
  6. @Entity  
  7. @Table(name="_teacher")  
  8. public class Teacher {  
  9. [……]  
  10. }  

Xml:

  1. <class name="Student" table="_student">  

九、 字段名和屬性相同

Annotation:默認爲@Basic

注意:如果在成員屬性沒有加入任何註解,則默認在前面加入了@Basic

 

Xml中不用寫column

 

十、 字段名和屬性名不同

Annotation:使用@Column(name=”columnName”)進行註解

         例如:

  1. /** 
  2.  * @Entity 表示下面的這個Teacher是一個實體類 
  3.  * @Table 表示映射到數據表中的表名,其中的name參數表示"表名稱" 
  4.  * @Column 表示實體類成員屬性映射數據表中的字段名,其中name參數指定一個新的字段名 
  5.  * @Id 表示主鍵Id 
  6.  */  
  7. @Entity  
  8. @Table(name="_teacher")  
  9. public class Teacher {  
  10.   
  11.     private int id;   
  12.     private String name;      
  13.     private String title;  
  14.   
  15.     //設置主鍵使用@Id  
  16.     @Id  
  17.     public int getId() {  
  18.         return id;  
  19.     }  
  20.   
  21.     @Column(name="_name")//字段名與屬性不同時  
  22.     public String getName() {  
  23.         return name;  
  24.     }  
  25. ……  

Xml:

  1. <property name="name" column="_name"/>  


十一、 不需要(持久化)psersistence的字段

就是不實體類的某個成員屬性不需要存入數據庫中

Annotation:使用@Transient 進行註解就可以了。

例如:

  1. @Transient  
  2.     public String getTitle() {  
  3.         return title;  
  4. }  

Xml:不寫(就是不需要對這個成員屬性進行映射)

十二、 映射日期與時間類型,指定時間精度

Annotation:使用@Temporal(value=TemporalType)來註解表示日期和時間的註解

            其中TemporalType有三個值:TemporalType.TIMESTAMP 表示yyyy-MM-dd HH:mm:ss

                                     TemporalType.DATE      表示yyyy-MM-dd

                                     TemporalType.TIME      表示HH:mm:ss


  1. @Temporal(value=TemporalType.DATE)  
  2.     public Date getBirthDate() {  
  3.         return birthDate;  
  4. }  

注意:當使用註解時,屬性爲value時,則這個屬性名可以省略,例如:@Temporal(TemporalType)

Xml:使用type屬性指定hibernate類型

  1. <property name="birthDate" type="date"/>  

    注意:hibernate日期時間類型有:date, time, timestamp,當然您也可以使用Java包裝類

        

十二、 映射枚舉類型

 

Annotation:使用@Enumerated(value=EnumType)來註解表示此成員屬性爲枚舉映射到數據庫

        其中EnumType有二個值:①EnumType.STRING  表示直接將枚舉名稱存入數據庫

                             ②EnumType.ORDINAL 表示將枚舉所對應的數值存入數據庫

Xml:映射非常的麻煩,先要定義自定義類型,然後再使用這個定義的類型……

 

第12課 使用hibernate工具類將對象模型生成關係模型

(也就是實體類生成數據庫中的表),完整代碼如下:

package com.wjt276.hibernate;

 

import org.hibernate.cfg.AnnotationConfiguration;

import org.hibernate.cfg.Configuration;

import org.hibernate.tool.hbm2ddl.SchemaExport;

 

/**

 * Hibernate工具<br/>

 * 將對象模型生成關係模型(將對象生成數據庫中的表)

 * 把hbm映射文件(或Annotation註解)轉換成DDL

 * 生成數據表之前要求已經存在數據庫

 * 注:這個工具類建立好後,以後就不用建立了。以後直接Copy來用。

 * @author wjt276

 * @version 1.0 2009/10/16

 */

public classExportDB {

    public static voidmain(String[] args){

        /*org.hibernate.cfg.Configuration類的作用:

         *讀取hibernate配置文件(hibernate.cfg.xml或hiberante.properties)的.

         *new Configuration()默認是讀取hibernate.properties

         *所以使用newConfiguration().configure();來讀取hibernate.cfg.xml配置文件

         */

        Configuration cfg = new AnnotationConfiguration().configure();

        /*org.hibernate.tool.hbm2ddl.SchemaExport工具類:

         *需要傳入Configuration參數

         *此工具類可以將類導出生成數據庫表

         */

        SchemaExport export = newSchemaExport(cfg);

        /** 開始導出

         *第一個參數:script是否打印DDL信息

         *第二個參數:export是否導出到數據庫中生成表

         */

        export.create(truetrue);

    }}

運行剛剛建立的ExportDB類中的main()方法,進行實際的導出類。

 

 

 

第13課 ID主鍵生成策略

一、 Xml方式

<id>標籤必須配置在<class>標籤內第一個位置。由一個字段構成主鍵,如果是複雜主鍵<composite-id>標籤

被映射的類必須定義對應數據庫表主鍵字段。大多數類有一個JavaBeans風格的屬性, 爲每一個實例包含唯一的標識。<id> 元素定義了該屬性到數據庫表主鍵字段的映射。

<id

       name="propertyName"                                          (1)

       type="typename"                                              (2)

       column="column_name"                                         (3)

       unsaved-value="null|any|none|undefined|id_value"             (4)

       access="field|property|ClassName"                            (5)

       node="element-name|@attribute-name|element/@attribute|.">

 

        <generatorclass="generatorClass"/>

</id>

(1) name (可選): 標識屬性的名字(實體類的屬性)。

(2) type (可選): 標識Hibernate類型的名字(省略則使用hibernate默認類型),也可以自己配置其它hbernate類型(integer, long, short, float,double, character, byte, boolean, yes_no, true_false)

(2) length(可選):當type爲varchar時,設置字段長度

(3) column (可選 - 默認爲屬性名): 主鍵字段的名字(省略則取name爲字段名)。

(4) unsaved-value (可選 - 默認爲一個切合實際(sensible)的值): 一個特定的標識屬性值,用來標誌該實例是剛剛創建的,尚未保存。 這可以把這種實例和從以前的session中裝載過(可能又做過修改--譯者注) 但未再次持久化的實例區分開來。

(5) access (可選 - 默認爲property): Hibernate用來訪問屬性值的策略。

如果 name屬性不存在,會認爲這個類沒有標識屬性。

unsaved-value 屬性在Hibernate3中幾乎不再需要。

還有一個另外的<composite-id>定義可以訪問舊式的多主鍵數據。 我們強烈不建議使用這種方式。

<generator>元素(主鍵生成策略)

主鍵生成策略是必須配置

用來爲該持久化類的實例生成唯一的標識。如果這個生成器實例需要某些配置值或者初始化參數, 用<param>元素來傳遞。

<id name="id"type="long" column="cat_id">

       <generator class="org.hibernate.id.TableHiLoGenerator">

               <param name="table">uid_table</param>

               <param name="column">next_hi_value_column</param>

       </generator>

</id>

所有的生成器都實現org.hibernate.id.IdentifierGenerator接口。 這是一個非常簡單的接口;某些應用程序可以選擇提供他們自己特定的實現。當然, Hibernate提供了很多內置的實現。下面是一些內置生成器的快捷名字:

increment

用於爲long, short或者int類型生成 唯一標識。只有在沒有其他進程往同一張表中插入數據時才能使用。 在集羣下不要使用。

identity

對DB2,MySQL, MS SQL Server,Sybase和HypersonicSQL的內置標識字段提供支持。 返回的標識符是long, short 或者int類型的。 (數據庫自增)

sequence

在DB2,PostgreSQL, Oracle, SAPDB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是long, short或者 int類型的。(數據庫自增)

hilo

使用一個高/低位算法高效的生成long, short 或者 int類型的標識符。給定一個表和字段(默認分別是 hibernate_unique_key和next_hi)作爲高位值的來源。 高/低位算法生成的標識符只在一個特定的數據庫中是唯一的。

seqhilo

使用一個高/低位算法來高效的生成long, short 或者 int類型的標識符,給定一個數據庫序列(sequence)的名字。

uuid

用一個128-bit的UUID算法生成字符串類型的標識符, 這在一個網絡中是唯一的(使用了IP地址)。UUID被編碼爲一個32位16進制數字的字符串,它的生成是由hibernate生成,一般不會重複。

UUID包含:IP地址,JVM的啓動時間(精確到1/4秒),系統時間和一個計數器值(在JVM中唯一)。 在Java代碼中不可能獲得MAC地址或者內存地址,所以這已經是我們在不使用JNI的前提下的能做的最好實現了

guid

在MS SQL Server 和 MySQL 中使用數據庫生成的GUID字符串。

native

根據底層數據庫的能力選擇identity,sequence 或者hilo中的一個。(數據庫自增)

assigned

讓應用程序在save()之前爲對象分配一個標示符。這是 <generator>元素沒有指定時的默認生成策略。(如果是手動分配,則需要設置此配置)

select

通過數據庫觸發器選擇一些唯一主鍵的行並返回主鍵值來分配一個主鍵。

foreign

使用另外一個相關聯的對象的標識符。通常和<one-to-one>聯合起來使用。

二、 annotateon方式

使用@GeneratedValue(strategy=GenerationType)註解可以定義該標識符的生成策略

Strategy有四個值:

①     、AUTO- 可以是identity column類型,或者sequence類型或者table類型,取決於不同的底層數據庫.

相當於native

②     、TABLE- 使用表保存id值

③     、IDENTITY- identity column

④     、SEQUENCE- sequence

注意:auto是默認值,也就是說沒有後的參數則表示爲auto

1、AUTO默認

@Id

    @GeneratedValue

    public int getId() {

        return id;

}

@Id

@GeneratedValue(strategy=GenerationType.AUTO)

public int getId() {

return id;

}

1、  對於mysql,使用auto_increment

2、  對於oracle使用hibernate_sequence(名稱固定)

2、IDENTITY

@Id

    @GeneratedValue(strategy=GenerationType.IDENTITY)

    public int getId() {

        return id;

}

對DB2,MySQL, MS SQL Server,Sybase和HypersonicSQL的內置標識字段提供支持。 返回的標識符是long, short 或者int類型的。 (數據庫自增)

注意:此生成策略不支持Oracle

 

3、SEQUENCE

    @Id

    @GeneratedValue(strategy=GenerationType.SEQUENCE)

    public int getId() {

        return id;

    }

在DB2,PostgreSQL,Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是long, short或者 int類型的。(數據庫自增)

注意:此生成策略不支持MySQL

 

4、爲Oracle指定定義的Sequence

    a)、首先需要在實體類前面申明一個Sequence如下:

        方法:@SequenceGenerator(name="SEQ_Name",sequenceName="SEQ_DB_Name")

            參數注意:SEQ_Name:表示爲申明的這個Sequence指定一個名稱,以便使用

                    SEQ_DB_Name:表示爲數據庫中的Sequence指定一個名稱。

                    兩個參數的名稱可以一樣。

@Entity

@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")

public class Teacher {

……

}

b)、然後使用@GeneratedValue註解

    方法:@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="SEQ_Name")

    參數:strategy:固定爲GenerationType.SEQUENCE

         Generator:在實體類前面申明的sequnce的名稱

@Entity

@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")

public class Teacher {

    private int id;

   

    @Id

    @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")

    public int getId() {

        return id;

}}

 

5、TABLE - 使用表保存id值

  原理:就是在數據庫中建立一個表,這個表包含兩個字段,一個字段表示名稱,另一個字段表示值。每次在添加數據時,使用第一個字段的名稱,來取值作爲添加數據的ID,然後再給這個值累加一個值再次存入數據庫,以便下次取出使用。

Table主鍵生成策略的定義:

@javax.persistence.TableGenerator(

        name="Teacher_GEN",              //生成策略的名稱

        table="GENERATOR_TABLE",     //在數據庫生成表的名稱

        pkColumnName = "pk_key",     //表中第一個字段的字段名 類型爲varchar,key

        valueColumnName = "pk_value",    //表中第二個字段的字段名 int ,value

        pkColumnValue="teacher",     //這個策略中使用該記錄的第一個字段的值(key值)

        initialValue = 1,                //這個策略中使用該記錄的第二個字段的值(value值)初始化值

        allocationSize=1                  //每次使用數據後累加的數值

)

這樣執行後,會在數據庫建立一個表,語句如下:

 create tableGENERATOR_TABLE (pk_key varchar(255),pk_value integer )

結構:


並且表建立好後,就插入了一個記錄,如下:

    

注:這條記錄的pk_value值爲2,是因爲剛剛做例程序時,已經插入一條記錄了。初始化時爲1。

使用TABLE主鍵生成策略:

@Entity

@javax.persistence.TableGenerator(

        name="Teacher_GEN",              //生成策略的名稱

        table="GENERATOR_TABLE",     //在數據庫生成表的名稱

        pkColumnName = "pk_key",     //表中第一個字段的字段名 類型爲varchar,key

        valueColumnName = "pk_value",    //表中第二個字段的字段名 int ,value

        pkColumnValue="teacher",     //這個策略中使用該記錄的第一個字段的值(key值)

        initialValue = 1,                //這個策略中使用該記錄的第二個字段的值(value值)初始化值

        allocationSize=1                 //每次使用數據後累加的數值

)

public class Teacher {

    private int id;

    @Id

    @GeneratedValue(strategy=GenerationType.TABLE,generator="Teacher_GEN")

    public int getId() {

        return id;}}

注意:這樣每次在添加Teacher記錄時,都會先到GENERATOR_TABLE表取pk_key=teacher的記錄後,使用pk_value值作爲記錄的主鍵。然後再給這個pk_value字段累加1,再存入到GENERATOR_TABLE表中,以便下次使用。

    這個表可以給無數的表作爲主鍵表,只是添加一條記錄而以(需要保證table、pkColumnName、valueColumnName三個屬性值一樣就可以了。),這個主鍵生成策略可以跨數據庫平臺。

三、 聯合主鍵

     複合主鍵(聯合主鍵):多個字段構成唯一性。

1、xml方式

a) 實例場景:覈算期間

// 覈算期間

public class FiscalYearPeriod {   

    private int fiscalYear; //覈算年

    private int fiscalPeriod; //覈算月  

    private Date beginDate; //開始日期  

    private Date endDate; //結束日期

    private String periodSts; //狀態

    public int getFiscalYear() {

        return fiscalYear;

    }

    public void setFiscalYear(int fiscalYear) {

        this.fiscalYear = fiscalYear;

    }

    public int getFiscalPeriod(){  return fiscalPeriod;}

    public void setFiscalPeriod(int fiscalPeriod) {

        this.fiscalPeriod =fiscalPeriod;

    }

    public DategetBeginDate() {return beginDate;}

    public void setBeginDate(DatebeginDate) {  this.beginDate = beginDate; }

    public Date getEndDate(){return endDate;}

    public void setEndDate(DateendDate) {  this.endDate = endDate; }

    public StringgetPeriodSts() {  return periodSts;}

    public voidsetPeriodSts(String periodSts) {this.periodSts = periodSts;}

}

複合主鍵的映射,一般情況把主鍵相關的屬性抽取出來單獨放入一個類中。而這個類是有要求的:必需實現序列化接口(java.io.Serializable)(可以保存到磁盤上),爲了確定這個複合主鍵類所對應對象的唯一性就會產生比較,對象比較就需要複寫對象的hashCode()、equals()方法(複寫方法如下圖片),然後在類中引用這個複合主鍵類

 


 

 

b) 複合主鍵類:

複合主鍵必需實現java.io.Serializable接口

public class FiscalYearPeriodPKimplements java.io.Serializable {

  private int fiscalYear;//覈算年 

  private int fiscalPeriod;//覈算月

  public int getFiscalYear() {

      return fiscalYear;

  }

  public void setFiscalYear(int fiscalYear) {

      this.fiscalYear = fiscalYear;

  }

  public int getFiscalPeriod(){

      return fiscalPeriod;

  }

  public void setFiscalPeriod(int fiscalPeriod) {

      this.fiscalPeriod =fiscalPeriod;

  }  

  @Override

  public int hashCode() {

      final int prime = 31;

      int result = 1;

      result = prime* result + fiscalPeriod;

      result = prime* result + fiscalYear;

      return result;

  }

  @Override

  public boolean equals(Object obj){

      if (this == obj)

          return true;

      if (obj == null)

          return false;

      if (getClass() !=obj.getClass())

          return false;

      FiscalYearPeriodPKother = (FiscalYearPeriodPK) obj;

      if (fiscalPeriod != other.fiscalPeriod)

          return false;

      if (fiscalYear != other.fiscalYear)

          return false;

      return true;

  }}

c) 實體類:(中引用了複合主鍵類)

public class FiscalYearPeriod{

    private FiscalYearPeriodPK fiscalYearPeriodPK;//引用 複合主鍵類  

    private Date beginDate;//開始日期   

    private Date endDate;//結束日期 

    private String periodSts;//狀態 

    public FiscalYearPeriodPK getFiscalYearPeriodPK() {

        return fiscalYearPeriodPK;

    }

    public void setFiscalYearPeriodPK(FiscalYearPeriodPKfiscalYearPeriodPK) {

        this.fiscalYearPeriodPK = fiscalYearPeriodPK;

    }

………………

 

d) FiscalYearPeriod.hbm.xml映射文件

<hibernate-mapping>

    <class name="com.bjsxt.hibernate.FiscalYearPeriod"table="t_fiscal_year_period">

        <composite-id name="fiscalYearPeriodPK">

            <key-property name="fiscalYear"/>

            <key-property name="fiscalPeriod"/>

        </composite-id>

        <property name="beginDate"/>

        <property name="endDate"/>

        <property name="periodSts"/>

    </class>

</hibernate-mapping>

e) 導出數據庫輸出SQL語句:

create table t_fiscalYearPeriod (fiscalYear integer not null, fiscalPeriodinteger not null, beginDate datetime, endDate datetime, periodSts varchar(255),primary key (fiscalYear, fiscalPeriod))//實體映射到數據就是兩個字段構成複合主鍵

f) 數據庫表結構:



g) 複合主鍵關聯映射數據存儲:

    session =HibernateUtils.getSession();

    tx =session.beginTransaction();

 

    FiscalYearPeriod fiscalYearPeriod = new FiscalYearPeriod();

           

    //構造複合主鍵

    FiscalYearPeriodPK pk = new FiscalYearPeriodPK();

    pk.setFiscalYear(2009);

    pk.setFiscalPeriod(11);

           

    fiscalYearPeriod.setFiscalYearPeriodPK(pk);//爲對象設置複合主鍵

    fiscalYearPeriod.setEndDate(new Date());

    fiscalYearPeriod.setBeginDate(new Date());

    fiscalYearPeriod.setPeriodSts("Y");

       

    session.save(fiscalYearPeriod);        

 

h) 執行輸出SQL語句:

Hibernate: insert into t_fiscalYearPeriod (beginDate, endDate, periodSts,fiscalYear, fiscalPeriod) values (?, ?, ?, ?, ?)

注:如果再存入相同複合主鍵的記錄,就會出錯。

i) 數據的加載:

    數據加載非常簡單,只是主鍵是一個對象而以,不是一個普通屬性。

 

2、annotation方式

下面是定義組合主鍵的幾種語法:

將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id

將組件的屬性註解爲@EmbeddedId

將類註解爲@IdClass,並將該實體中所有屬於主鍵的屬性都註解爲@Id

a) 將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id

組件類:

@Embeddable

public class TeacherPK implementsjava.io.Serializable{

    private int id;

    private String name;

    public int getId() {return id;  }

    public void setId(int id) {this.id = id;}

    public String getName() {   return name;}

    public void setName(Stringname) {  this.name = name;}

    @Override

    public boolean equals(Object o) {       ……}

    @Override

    public int hashCode() { return this.name.hashCode();    }

}

將組件類的屬性註解爲@Id,實體類中組件的引用

@Entity

public class Teacher {

  private TeacherPK pk;

  private String title;

 

  @Id

  public TeacherPK getPk(){

      return pk;

}}

 

b)  將組件的屬性註解爲@EmbeddedId

注意:只需要在實體類中表示覆合主鍵屬性前註解爲@Entity,表示此主鍵是一個複合主鍵

       注意了,複合主鍵類不需要任何的注意。

@Entity

public class Teacher {

   private TeacherPK pk;

   private String title;  

 

   @EmbeddedId

   public TeacherPK getPk(){

       return pk;

   }}

c)  類註解爲@IdClass,主鍵的屬性都註解爲@Id

需要將複合主鍵類建立好,不需要進行任何註解

在實體類中不需要進行復合主鍵類的引用

需要在實體類前面註解爲@IdClass,並且指定一個value屬性,值爲複合主鍵類的class

需要在實體類中進行復合主鍵成員屬性前面註解爲@Id

如下:

@Entity

@IdClass(TeacherPK.class)

public class Teacher {

  //private TeacherPK pk;//不再需要

  private int id;

  private String name;   

  @Id

  public int getId() {return id;  }

  public void setId(int id) { this.id = id;   }

 

  @Id

  public String getName() {return name;}

  public void setName(Stringname) {this.name = name;

  }}

 

 

 

第14課 Hibernate核心開發接口(重點)

一、 Configuration(AnnotationConfiguration)

作用:進行配置信息的管理

目標:用來產生SessionFactory

可以在configure方法中指定hibernate配置文件,默認(不指定)時在classpath下加載hibernate.cfg.xml文件

加載默認的hibernate的配置文件

sessionFactory = newAnnotationConfiguration().configure().buildSessionFactory();

加載指定hibernate的配置文件

sessionFactory=newnnotationConfiguration().configure("hibernate.xml").buildSessionFactory();

只需要關注一個方法:buildSessionFactory();

二、 SessionFactory

作用:主要用於產生Session的工廠(數據庫連接池)

當它產生一個Session時,會從數據庫連接池取出一個連接,交給這個Session

Session session = sessionFactory.getCurrentSession();

         並且可以通過這個Session取出這個連接

         關注兩個方法:

         getCurrentSession():表示當前環境沒有Session時,則創建一個,否則不用創建

         openSession():     表示創建一個Session(3.0以後不常用),使用後需要關閉這個Session

         兩方法的區別:

           ①、openSession永遠是每次都打開一個新的Session,而getCurrentSession不是,是從上下文找、只有當前沒有Session時,才創建一個新的Session

           ②、OpenSession需要手動close,getCurrentSession不需要手動close,事務提交自動close

           ③、getCurrentSession界定事務邊界

上下文:

所指的上下文是指hibernate配置文件(hibernate.cfg.xml)中的“current_session_context_class”所指的值:(可取值:jta|thread|managed|custom.Class)

<property name="current_session_context_class">thread</property>

         常用的是:①、thread:是從上下文找、只有當前沒有Session時,才創建一個新的Session,主要從數據界定事務

              ②、jta:主要從分佈式界定事務,運行時需要Application Server來支持(Tomcat不支持)

             ③、managed:不常用

④、custom.Class:不常用

           

三、 Session

1、    管理一個數據庫的任務單元

2、    save();

session.save(Object)

session的save方法是向數據庫中保存一個對象,這個方法產生對象的三種狀態

3、    delete() 

session.delete(Object)

   Object對象需要有ID

   對象刪除後,對象狀態爲Transistent狀態

4、    load()

格式: Session.load(Class arg0,Serializable arg1) throws HibernateException

    *arg0:需要加載對象的類,例如:User.class

    *arg1:查詢條件(實現了序列化接口的對象):例"4028818a245fdd0301245fdd06380001"字符串已經實現了序列化接口。如果是數值類類型,則hibernate會自動使用包裝類,例如 1

    * 此方法返回類型爲Object,但返回的是代理對象。

    * 執行此方法時不會立即發出查詢SQL語句。只有在使用對象時,它才發出查詢SQL語句,加載對象。

    * 因爲load方法實現了lazy(稱爲延遲加載、賴加載)

    * 延遲加載:只有真正使用這個對象的時候,才加載(才發出SQL語句)

    *hibernate延遲加載實現原理是代理方式。

    * 採用load()方法加載數據,如果數據庫中沒有相應的記錄,則會拋出異常對象不找到(org.hibernate.ObjectNotFoundException)

    try {

        session =sf.openSession();

        session.beginTransaction();

       

         User user =(User)session.load(User.class,1);

           

        //只有在使用對象時,它才發出查詢SQL語句,加載對象。

        System.out.println("user.name=" + user.getName());

           

        //因爲此的user爲persistent狀態,所以數據庫進行同步爲龍哥。

        user.setName("發哥");

           

        session.getTransaction().commit();

    } catch (HibernateExceptione) {

        e.printStackTrace();

        session.getTransaction().rollback();

    } finally{

            if (session != null){

                if(session.isOpen()){

                    session.close();

                }

            }

    }

5、    Get()     

格式:Session.get(Class arg0,Serializable arg1)方法

* arg0:需要加載對象的類,例如:User.class

    * arg1:查詢條件(實現了序列化接口的對象):

例"4028818a245fdd0301245fdd06380001"字符串已經實現了序列化接口。如果是基數類型,則hibernate會自動轉換成包裝類,如 1

    返回值: 此方法返回類型爲Object,也就是對象,然後我們再強行轉換爲需要加載的對象就可以了。

            如果數據不存在,則返回null;

    注:執行此方法時立即發出查詢SQL語句。加載User對象

加載數據庫中存在的數據,代碼如下:

try {

          session =sf.openSession();

          session.beginTransaction();

                 

           * 此方法返回類型爲Object,也就是對象,然後我們再強行轉換爲需要加載的對象就可以了。

              如果數據不存在,則返回null

           * 執行此方法時立即發出查詢SQL語句。加載User對象。

           */

          User user = (User)session.get(User.class, 1);

         

          //數據加載完後的狀態爲persistent狀態。數據將與數據庫同步。

          System.out.println("user.name=" + user.getName());

         

          //因爲此的user爲persistent狀態,所以數據庫進行同步爲龍哥。

          user.setName("龍哥");

         

          session.getTransaction().commit();

      } catch(HibernateException e) {

          e.printStackTrace();

          session.getTransaction().rollback();

      } finally{

          if (session != null){

              if(session.isOpen()){

                  session.close();

              }

          }

6、    load()與get()區別

①、 不存在對應記錄時表現不一樣;

②、 load返回的是代理對象,等到真正使用對象的內容時才發出sql語句,這樣就要求在第一次使用對象時,要求session處於open狀態,否則出錯

③、 get直接從數據庫加載,不會延遲加載

get()和load()只根據主鍵查詢,不能根據其它字段查詢,如果想根據非主鍵查詢,可以使用HQL

7、    update()

①        、用來更新detached對象,更新完成後轉爲爲persistent狀態(默認更新全部字段)

②        更新transient對象會報錯(沒有ID)

③        更新自己設定ID的transient對象可以(默認更新全部字段)

④        persistent狀態的對象,只要設定字段不同的值,在session提交時,會自動更新(默認更新全部字段)

⑤        更新部分更新的字段(更改了哪個字段就更新哪個字段的內容)

a)       方法1:update/updatable屬性

 xml:設定<property>標籤的update屬性,設置在更新時是否參數更新

<property name="name" update="false"/>

注意:update可取值爲true(默認):參與更新;false:更新時不參與更新

annotateon:設定@Column的updatable屬性值,true參與更新,false:不參與更新

    @Column(updatable=false)

    public String getTitle(){return title;}

注意:此種方法很少用,因爲它不靈活

b)       方法二:dynamic-update屬性

注意:此方法目前只適合xml方式,JAP1.0annotation沒有對應的

在實體類的映射文件中的<class>標籤中,使用dynamic-update屬性,true:表示修改了哪個字段就更新哪個字段,其它字段不更新,但要求是同一個session(不能跨session),如果跨了session同樣會更新所有的字段內容。

<class name="com.bjsxt.Student" dynamic-update="true">

          代碼:

@Test

  public void testUpdate5() {    

      Sessionsession = sessionFactory.getCurrentSession();

      session.beginTransaction();

      Student s =(Student)session.get(Student.class, 1);

      s.setName("zhangsan5");

      //提交時,會只更新name字段,因爲此時的s爲persistent狀態

      session.getTransaction().commit();

      s.setName("z4");

      Sessionsession2 = sessionFactory.getCurrentSession();

      session2.beginTransaction();

      //更新時,會更新所有的字段,因爲此時的s不是persistent狀態

      session2.update(s);

      session2.getTransaction().commit(); }

           如果需要跨session實現更新修改的部分字段,需要使用session.merget()方法,合併字段內容

@Test

  public void testUpdate6() {    

      Sessionsession = sessionFactory.getCurrentSession();

      session.beginTransaction();

      Student s =(Student)session.get(Student.class, 1);

      s.setName("zhangsan6");

      session.getTransaction().commit();

      s.setName("z4");

      Sessionsession2 = sessionFactory.getCurrentSession();

      session2.beginTransaction();

      session2.merge(s);

      session2.getTransaction().commit()}

這樣雖然可以實現部分字段更新,但這樣會多出一條select語句,因爲在字段數據合併時,需要比較字段內容是否已變化,就需要從數據庫中取出這條記錄進行比較

c)      使用HQL(EJBQL)面向對象的查詢語言(建議)

  @Test

  public void testUpdate7() {    

      Sessionsession = sessionFactory.getCurrentSession();

      session.beginTransaction();

      Query q =session.createQuery(

"update Student s sets.name='z5' where s.id = 1");

      q.executeUpdate();

      session.getTransaction().commit();     

  }  

8、    saveOrUpdate()

在執行的時候hibernate會檢查,如果對象在數據庫中已經有對應的記錄(是指主鍵),則會更新update,否則會添加數據save

9、    clear()

清除session緩存

無論是load還是get,都會首先查找緩存(一級緩存,也叫session級緩存),如果沒有,纔會去數據庫查找,調用clear()方法可以強制清除session緩存

    Session session= sessionFactory.getCurrentSession();

    session.beginTransaction();

    Teacher t =(Teacher)session.load(Teacher.class, 1);

    System.out.println(t.getName());       

    session.clear();       

    Teacher t2 =(Teacher)session.load(Teacher.class, 1);

    System.out.println(t2.getName());

    session.getTransaction().commit();

注意:這樣就會發出兩條SELECT語句,如果把session.clear()去除,則只會發出一條SELECT語句,因爲第二次load時,是使用session緩存中ID爲1的對象,而這個對象已經在第一次load到緩存中 了。

10、 flush()

在hibernate中也存在flush這個功能,在默認的情況下session.commit()之前時,其實執行了一個flush命令。

Session.flush功能:

n         清理緩存;

n         執行sql(確定是執行SQL語句(確定生成update、insert、delete語句等),然後執行SQL語句。)

 

Session在什麼情況下執行flush:

①         默認在事務提交時執行;

注意:flush時,可以自己設定,使用session.setFlushMode(FlushMode)來指定。

  session.setFlushMode(FlushMode);

FlushMode的枚舉值:

l     FlushMode.ALWAYS:任務一條SQL語句,都會flush一次

l     FlushMode.AUTO  :自動flush(默認)

l     FlushMode.COMMIT: 只有在commit時才flush

l     FlushMode.MANUAL:手動flush。

l    FlushMode.NEVER :永遠不flush  此選項在性能優化時可能用,比如session取數據爲只讀時用,這樣就  

不需要與數據庫同步了

        注意:設置flush模式時,需要在session開啓事務之前設置。

②        可以顯示的調用flush;

③        在執行查詢前,如:iterate.

注:如果主鍵生成策略是uuid等不是由數據庫生成的,則session.save()時並不會發出SQL語句,只有flush時纔會發出SQL語句,但如果主鍵生成策略是native由數據庫生成的,則session.save的同時就發出SQL語句。

 

11、 evict()

例如:session.evict(user)

作用:從session緩存(EntityEntries屬性)中逐出該對象

但是與commit同時使用,會拋出異常

session = HibernateUtils.getSession();

tx = session.beginTransaction();

   

User1 user = new User1();

user.setName("李四");

user.setPassword("123");

user.setCreateTime(new Date());

user.setExpireTime(new Date());

           

//利用Hibernate將實體類對象保存到數據庫中,因爲user主鍵生成策略採用的是uuid,所以調用完成save後,只是將user納入session的管理,不會發出insert語句,但是id已經生成,session中的existsInDatabase狀態爲false

session.save(user);

               

session.evict(user);//從session緩存(EntityEntries屬性)中逐出該對象

//無法成功提交,因爲hibernate在清理緩存時,在session的臨時集合(insertions)中取出user對象進行insert操作後需要更新entityEntries屬性中的existsInDatabase爲true,而我們採用evict已經將user從session中逐出了,所以找不到相關數據,無法更新,拋出異常。

           

tx.commit();

 

解決在逐出session緩存中的對象不拋出異常的方法:

在session.evict()之前進行顯示的調用session.flush()方法就可以了。

session.save(user);

               

//flush後hibernate會清理緩存,會將user對象保存到數據庫中,將session中的insertions中的user對象清除,並且會設置session中的existsInDatabase狀態爲false

session.flush();

           

session.evict(user);//從session緩存(EntityEntries屬性)中逐出該對象

           

//可以成功提交,因爲hibernate在清理緩存時,在Session的insertions中集合中無法找到user對象所以不會發出insert語句,也不會更新session中existsInDatabase的狀態。

tx.commit();

 

 


第15課 持久化對象的三種狀態

一、 瞬時對象(Transient Object):

使用new操作符初始化的對象不是立刻就持久的。它們的狀態是瞬時的,也就是說它們沒有任何跟數據庫表相關聯的行爲,只要應用不再引用這些對象(不再被任何其它對象所引用),它們的狀態將會丟失,並由垃圾回收機制回收

二、 持久化對象(Persistent Object):

持久實例是任何具有數據庫標識的實例,它有持久化管理器Session統一管理,持久實例是在事務中進行操作的----它們的狀態在事務結束時同數據庫進行同步。當事務提交時,通過執行SQL的INSERT、UPDATE和DELETE語句把內存中的狀態同步到數據庫中。

三、 離線對象(Detached Object):

Session關閉之後,持久化對象就變爲離線對象。離線表示這個對象不能再與數據庫保持同步,它們不再受hibernate管理。

 

 


四、 三種狀態的區分:

1、  有沒有ID,(如果沒有則是Transient狀態)

2、  ID在數據庫中有沒有

3、  在內存裏有沒有(session緩存)

五、 總結:

Transient對象:隨時可能被垃圾回收器回收(在數據庫中沒有於之對應的記錄,應爲是new初始化),而執行save()方法後,就變爲Persistent對象(持久性對象),沒有納入session的管理

                            內存中一個對象,沒有ID,緩存中也沒有

Persistent對象:在數據庫有存在的對應的記錄,納入session管理。在清理緩存(髒數據檢查)的時候,會和數據庫同步。

                            內存中有、緩存中有、數據庫有(ID)

Detached對象:也可能被垃圾回收器回收掉(數據庫中存在對應的記錄,只是沒有任何對象引用它是指session引用),注引狀態經過Persistent狀態,沒有納入session的管理

內存有、緩存沒有、數據庫有(ID)

第16課 關係映射(重點)

         注意:這裏的關係是指:對象之間的關係並不是指數據庫的關係,-----紅色重要

         存在以下關係:

1、    一對一

u       單向(主鍵、外鍵)

u       雙向(主鍵、外鍵)

2、    一對多

u       單向

u       雙向

3、    多對一

u       單向

u       雙向

4、    多對多

u       單向

u       雙向

5、    集合映射

u       List

u       Set

u       Map

6、    繼承關係(不重要)

u       單表

u       多表

u       一張主表、多張子表

7、    組件映射

u       @Embeddable

u       @Embedded

 

一、 一對一關聯映射

²        兩個對象之間是一對一的關係,如Person-IdCard(人—身份證號)

²        有兩種策略可以實現一對一的關聯映射

Ø        主鍵關聯:即讓兩個對象具有相同的主鍵值,以表明它們之間的一一對應的關係;數據庫表不會有額外的字段來維護它們之間的關係,僅通過表的主鍵來關聯。

Ø        唯一外鍵關聯:外鍵關聯,本來是用於多對一的配置,但是如果加上唯一的限制之後,也可以用來表示一對一關聯關係。

 

對象模型

實體類:

/** 人-實體類 */

public class Person {

    private int id;

    private String name;

    public int getId() {return id;  }

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

    public void setName(Stringname) {this.name = name;}

}

 

/**身份證-實體類*/

public class IdCard {

    private int id;

    private String cardNo;

    public int getId() {return id;}

    public void setId(int id) {this.id = id;}

    public String getCardNo(){ return cardNo;}

    public void setCardNo(StringcardNo) {this.cardNo = cardNo;}

}

(一) 唯一外鍵關聯-單向(unilateralism)

1、 說明:

人—-> 身份證號(PersonàIdCard),從IdCard看不到Person對象

2、 對象模型

需要在Person類中持有IdCard的一個引用idCard,則IdCard中沒有Person的引用


3、 關係模型

關係模型目的:是實體類映射到關係模型(數據庫中),是要求persion中添加一個外鍵指向idcard


4、 實體類:

      注:IdCard是被引用對象,沒有變化。

         /** 人-實體類 */

public class Person {

    private int id;

    private String name;

private IdCard idCard;//引用IdCard對象

    public int getId() {return id;  }

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

    public void setName(String name){this.name = name;}

public IdCard getIdCard() { return idCard;}

    public void setIdCard(IdCardidCard) {this.idCard = idCard;}

}

 

5、 xml映射

IdCard實體類的映射文件:

     因爲IdCard是被引用的,所以沒有什麼特殊的映射

<hibernate-mapping>

  <class name="com.wjt276.hibernate.IdCard" table="t_idcard">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="cardNo"/>

  </class>

</hibernate-mapping>

Person實體類的映射文件

         在映射時需要添加一個外鍵的映射,就是指定IdCard的引用的映射。這樣映射到數據庫時,就會自動添加一個字段並作用外鍵指向被引用的表

<hibernate-mapping>

  <class name="com.wjt276.hibernate.Person" table="t_person">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="name"/> 

<!-- <many-to-one>:在多的一端(當前Person一端),加入一個外鍵(當前爲idCard)指向一的一端(當前IdCard),但多對一 關聯映射字段是可以重複的,所以需要加入一個唯一條件unique="true",這樣就可以此字段唯一了。-->

      <many-to-one name="idCard" unique="true"/>

  </class>

</hibernate-mapping>

注意:這裏的<many-to-one>標籤中的name屬性值並不是數據庫中的字段名,而是Person實體類中引用IdCard對象成員屬性的getxxx方法後面的xxx(此處是getIdCard,所以是idCard),要求第一個字段小寫。如果不指定column屬性,則數據庫中的字段名同name值

6、 annotateon註解映射

注意IdCard是被引用對象,除正常註解,無需要其它註解

/**身份證*/

@Entity

public class IdCard {

  private int id;

  private String cardNo;

  @Id

  @GeneratedValue

  public int getId() {return id;}

  public void setId(int id) { this.id = id;}

  public String getCardNo(){return cardNo;}

  public void setCardNo(StringcardNo) {this.cardNo = cardNo;}

}

 

而引用對象的實體類需要使用@OneToOne進行註解,來表面是一對一的關係

再使用@JoinColumn註解來爲數據庫表中這個外鍵指定個字段名稱就可以了。如果省略@JoinColumn註解,則hibernate會自動爲其生成一個字段名(好像是:被引用對象名稱_被引用對象的主鍵ID)

/** 人-實體類 */

@Entity

public class Person {

  private int id;

  private IdCard idCard;//引用IdCard對象  

  private String name;   

  @Id

  @GeneratedValue

  public int getId() {return id;}

  @OneToOne//表示一對一的關係

  @JoinColumn(name="idCard")//爲數據中的外鍵指定個名稱

  public IdCard getIdCard(){ return idCard;}

  public String getName() {return name;}

  public void setId(int id) {this.id = id;}

  public void setIdCard(IdCardidCard) {this.idCard = idCard;}

  public void setName(Stringname) {this.name = name;}   

}

7、 生成的SQL語句:

    create tableIdCard (

        id integernot null auto_increment,

        cardNo varchar(255),

        primary key(id)

    )

    create tablePerson (

        id integernot null auto_increment,

        namevarchar(255),

        idCardinteger,//新添加的外鍵

        primary key(id)

    )

    alter tablePerson

        add indexFK8E488775BE010483 (idCard),

        addconstraint FK8E488775BE010483

        foreign key(idCard) //外鍵

        referencesIdCard (id)//引用IdCard的id字段



8、 存儲測試

Session session = sf.getCurrentSession();      

IdCard idCard = new IdCard();

idCard.setCardNo("88888888888888888888888");       

session.beginTransaction();

// 如果先不保存idCard,則出拋出Transient異常,因爲idCard不是持久化狀態。

session.save(idCard);      

Person person = new Person();

person.setName("菜10");

person.setIdCard(idCard);

session.save(person);

session.getTransaction().commit();

 

(二) 唯一外鍵關聯-雙向

1、 說明:

人<—-> 身份證號(Person<->IdCard)雙向:互相持有對方的引用

2、 對象模型:



3、 關係模型:

關係模型沒有任務變化,同單向

4、 實體類:

實體類,只是相互持有對象的引用,並且要求getter和setter方法

5、 xml映射

Person實體類映射文件:同單向的沒有變化

IdCard實體類映射文件:如果使用同樣的方法映射,這樣就會在表中也添加一個外鍵指向對象,但對象已經有一個外鍵指向自己了,這樣就造成了庸字段,因爲不需要在表另外添加字段,而是讓hibernate在加載這個對象時,會根據對象的ID到對方的表中查詢外鍵等於這個ID的記錄,這樣就把對象加載上來了。也同樣需要使用<one-to-one>標籤來映射,但是需要使用property-ref屬性來指定對象持有你自己的引用的成員屬性名稱(是gettxxxx後面的名稱),這樣在生成數據庫表時,就不會再添加一個多於的字段了。數據加載時hibernate會根據這些配置自己加載數據

    <class name="com.wjt276.hibernate.IdCard" table="idcard">

        <id name="id" column="id">

            <generator class="native"/></id>

        <property name="cardNo"/>

        <!--<one-to-one>標籤:告訴hibernate如何加載其關聯對象

            property-ref屬性:是根據哪個字段進行比較加載數據 -->

        <one-to-one name="person" property-ref="idCard"/>

    </class>

一對一 唯一外鍵 關聯映射 雙向 需要在另一端(當前IdCard),添加<one-to-one>標籤,指示hibernate如何加載其關聯對象(或引用對象),默認根據主鍵加載(加載person),外鍵關聯映射中,因爲兩個實體採用的是person的外鍵來維護的關係,所以不能指定主鍵加載person,而要根據person的外鍵加載,所以採用如下映射方式:

    <!--<one-to-one>標籤:告訴hibernate如何加載其關聯對象

            property-ref屬性:是根據哪個字段進行比較加載數據 -->

        <one-to-one name="person" property-ref="idCard"/>

6、 annotateon註解映射

Person註解映射同單向一樣

IdCard註解映射如下:使用@OneToOne註解來一對一,但這樣會在表中多加一個字段,因爲需要使用對象的外鍵來加載數據,所以使用屬性mappedBy屬性在實現這個功能

@Entity

public class IdCard {

  private int id;

  private String cardNo;

  private Person person; 

  //mappedBy:在指定當前對象在被Person對象的idCard做了映射了

  //此值:當前對象持有引用對象中引用當前對象的成員屬性名稱(getXXX後的名稱)

  //因爲Person對象的持有IdCard對象的方法是getIdCard()因爲需要小寫,所以爲idCard

  @OneToOne(mappedBy="idCard")

  public Person getPerson(){return person;}

  public void setPerson(Person person){this.person = person;}

  @Id

  @GeneratedValue

  public int getId() {return id;}

  public void setId(int id) { this.id = id;}

  public String getCardNo(){return cardNo;}

  public void setCardNo(StringcardNo) {this.cardNo = cardNo;}

}

7、 生成SQL語句

因爲關係模型沒有變化,也就是數據庫的結構沒有變化,只是在數據加載時需要相互加載對方,這由hibernate來完成。因爲生成的sql語句同單向一樣

8、 存儲測試

存儲同單向一樣

9、 總結:

規律:凡是雙向關聯,必設mappedBy

 

(三) 主鍵關聯-單向(不重要)

主鍵關聯:即讓兩個對象具有相同的主鍵值,以表明它們之間的一一對應的關係;數據庫表不會有額外的字段來維護它們之間的關係,僅通過表的主鍵來關聯。

 

1、 說明:

人—-> 身份證號(PersonàIdCard),從IdCard看不到Person對象

2、 對象模型

站在人的角度來看,對象模型與唯一外鍵關聯一個,只是關係模型不同


3、 關係模型

因爲是person引用idcard,所以idcard要求先有值。而person的主鍵值不是自己生成的。而是參考idcard的值,person表中即是主鍵,同時也是外鍵


4、 實體類:

實體類同 一對一 唯一外鍵關聯的實體類一個,在person對象中持有idcard對象的引用(代碼見唯一外鍵關係)

5、 xml映射

IdCard映射文件,先生成ID

    <class name="com.wjt276.hibernate.IdCard" table="t_idcard">

        <id name="id"column="id">

            <generator class="native"/>

        </id>

        <property name="cardNo"/>

    </class>

 

Person實體類映射文件,ID是根據IdCard主鍵值   

<class name="com.wjt276.hibernate.Person"table="t_person">

        <id name="id"column="id">

<!--因爲主鍵不是自己生成的,而是作爲一個外鍵(來源於其它值),所以使用foreign生成策略

foreign:使用另外一個相關聯的對象的標識符,通常和<one-to-one>聯合起來使用。再使用元素<param>的屬性值指定相關聯對象(這裏Person相關聯的對象爲idCard,則標識符爲idCard的id)爲了能夠在加載person數據同時加載IdCard數據,所以需要使用一個標籤<one-to-one>來設置這個功能。  -->

            <generator class="foreign">

                <!-- 元素<param>屬性name的值是固定爲property -->

                <param name="property">idCard</param>

            </generator>

        </id>

        <property name="name"/>

        <!-- <one-to-one>標籤

        表示如何加載它的引用對象(這裏引用對象就指idCard這裏的name值是idCard),同時也說是一對一的關係。 默認方式是根據主鍵加載(把person中的主鍵取出再到IdCard中來取相關IdCard數據。) 我們也說過此主鍵也作爲一個外鍵引用 了IdCard,所以需要加一個數據庫限制(外鍵約束)constrained="true"     -->

        <one-to-one name="idCard"constrained="true"/> 

6、 annotateon註解映射

Person實體類註解

方法:只需要使用@OneToOne註解一對一關係,再使用@PrimaryKeyJoinColumn來註解主鍵關係映射。

@Entity

public class Person {

    private int id;

    private IdCard idCard;//引用IdCard對象  

    private String name;   

    @Id

    public int getId() {return id;}

    @OneToOne//表示一對一的關係

    @PrimaryKeyJoinColumn//註解主鍵關聯映射

    public IdCard getIdCard(){ return idCard;}

    public String getName() {return name;}

    public void setId(int id) {this.id = id;}

    public void setIdCard(IdCard idCard){this.idCard = idCard;}

    public void setName(Stringname) {this.name = name;}   

}

 

IdCard實體類,不需要持有對象的引用,正常註解就可以了。

7、 生成SQL語句

生成的兩個表並沒有多餘的字段,因爲是通過主鍵在關鍵的

    create tableIdCard (

        id integernot null auto_increment,

        cardNovarchar(255),

        primary key (id)

    )

    create tablePerson (

        id integernot null,

        namevarchar(255),

        primary key(id)

  )

alter table person

add index FK785BED805248EF3 (id),

add constraint FK785BED805248EF3

foreign key (id) references idcard (id)

    注意:annotation註解後,並沒有映射出外鍵關鍵的關聯,而xml可以映射,是主鍵關聯不重要

8、 存儲測試

session = HibernateUtils.getSession();

tx = session.beginTransaction();

IdCard idCard = new IdCard();

idCard.setCardNo("88888888888888888888888");

           

Person person = new Person();

person.setName("菜10");

person.setIdCard(idCard);

           

//不會出現TransientObjectException異常

//因爲一對一主鍵關鍵映射中,默認了cascade屬性。

session.save(person);

tx.commit();

 

9、 總結

讓兩個實體對象的ID保持相同,這樣可以避免多餘的字段被創建

<id name="id"column="id">

      <!—person的主鍵來源idcard,也就是共享idCard的主鍵-->

          <generator class="foreign">

              <param name="property">idCard</param>

          </generator>

      </id>

      <property name="name"/>

<!—one-to-one標籤的含義:指示hibernate怎麼加載它的關聯對象,默認根據主鍵加載

  constrained="true",表面當前主鍵上存在一個約束:person的主鍵作爲外鍵參照了idCard-->

      <one-to-one name="idCard" constrained="true"/>

(四) 主鍵關聯-雙向(不重要)

主鍵關聯:即讓兩個對象具有相同的主鍵值,以表明它們之間的一一對應的關係;數據庫表不會有額外的字段來維護它們之間的關係,僅通過表的主鍵來關聯。

主鍵關聯映射,實際是數據庫的存儲結構並沒有變化,只是要求雙方都可以持有對象引用,也就是說實體模型變化,實體類都相互持有對方引用。

另外映射文件也變化了。


1、 xml映射

Person實體類映射文件不變,

IdCard如下:

<class name="com.wjt276.hibernate.IdCard" table="t_idcard">

        <id name="id" column="id">

            <generator class="native"/> </id>

        <property name="cardNo"/>

<!—one-to-one標籤的含義:指示hibernate怎麼加載它的關聯對象(這裏的關聯對象爲person),默認根據主鍵加載-->

        <one-to-one name="person"/>

    </class>

2、 annotateon註解映射:

Person的註解不變,同主鍵單向註解

 

IdCard註解,只需要在持有對象引用的getXXX前加上

        @OneToOne(mappedBy="idCard") 如下:

@Entity

public class IdCard {

    private int id;

    private String cardNo;

    private Person person; 

    @OneToOne(mappedBy="idCard")

    public Person getPerson(){

        return person;

    }}

(五) 聯合主鍵關聯(Annotation方式)

實現上聯合主鍵的原理同唯一外鍵關聯-單向一樣,只是使用的是@JoinColumns,而不是@JoinColumn,實體類註解如下:

    @OneToOne

    @JoinColumns(

        {

            @JoinColumn(name="wifeId", referencedColumnName="id"),

            @JoinColumn(name="wifeName", referencedColumnName="name")

        }

    )

    public WifegetWife() {

        return wife;

    }

        注意:@oinColumns註解聯合主鍵一對一聯繫,然後再使用@JoinColumn來註解當前表中的外鍵字段名,並指定關聯哪個字段,使用referencedColumnName指定哪個字段的名稱

 

二、 component(組件)關聯映射

(一) Component關聯映射:

目前有兩個類如下:


         大家發現用戶與員工存在很多相同的字段,但是兩者有不可以是同一個類中,這樣在實體類中每次都要輸入很多信息,現在把聯繫信息抽取出來成爲一個類,然後在用戶、員工對象中引用就可以,如下:


值對象沒有標識,而實體對象具有標識,值對象屬於某一個實體,使用它重複使用率提升,而且更清析。

 

以上關係的映射稱爲component(組件)關聯映射

在hibernate中,component是某個實體的邏輯組成部分,它與實體的根本區別是沒有oid,component可以成爲是值對象(DDD)。

採用component映射的好處:它實現了對象模型的細粒度劃分,層次會更加分明,複用率會更高。

(二) User實體類:

public class User {

private int id;

  private String name;   

  private Contact contact;//值對象的引用   

  public int getId() {return id;}

  public void setId(int id) { this.id = id;}

  public String getName() {   return name;}

  public void setName(Stringname) {  this.name = name;}

  public ContactgetContact() {   return contact;}

  public void setContact(Contactcontact) {   this.contact = contact;}   

}

(三) Contact值對象:

public class Contact {

  private String email;  

  private String address;

  private String zipCode;

  private String contactTel;

  public String getEmail(){  return email;}

  public void setEmail(Stringemail) {    this.email = email; }

  public StringgetAddress() {return address;}

  public void setAddress(Stringaddress) {this.address = address;}

  public StringgetZipCode() {return zipCode;}

  public void setZipCode(StringzipCode) {this.zipCode = zipCode;}

  public StringgetContactTel() { return contactTel;}

  public voidsetContactTel(String contactTel){this.contactTel = contactTel;}

}

 

(四) xml--User映射文件(組件映射):

<hibernate-mapping>

  <class name="com.wjt276.hibernate.User" table="t_user">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="name" column="name"/>

      <!-- <component>標籤用於映射Component(組件)關係

          其內部屬性正常映射。

       -->

      <component name="contact">

          <property name="email"/>

          <property name="address"/>

          <property name="zipCode"/>

          <property name="contactTel"/>          

      </component>

  </class>

</hibernate-mapping>

(五) annotateon註解

使用@Embedded用於註解組件映射,表示嵌入對象的映射

@Entity

public class User {

    private int id;

    private String name;   

    private Contact contact;//值對象的引用   

    @Id

    @GeneratedValue

    public int getId() {    return id;}

 

    @Embedded//用於註解組件映射,表示嵌入對象的映射

    public ContactgetContact() {return contact;}

    public void setContact(Contactcontact) {this.contact = contact;}  

Contact類是值對象,不是實體對象,是屬於實體類的某一部分,因此沒有映射文件

(六) 導出數據庫輸出SQL語句:

    create table User (

        id integer not null auto_increment,

        address varchar(255),

        contactTel varchar(255),

        email varchar(255),

        zipCode varchar(255),

        name varchar(255),

        primary key (id)

)

(七) 數據表結構:


注:雖然實體類沒有基本聯繫信息,只是有一個引用,但在映射數據庫時全部都映射進來了。以後值對象可以重複使用,只要在相應的實體類中加入一個引用即可。

(八) 組件映射數據保存:

           session =HibernateUtils.getSession();

           tx =session.beginTransaction();

 

           User user= new User();

           user.setName("10");

          

           Contactcontact = new Contact();

           contact.setEmail("wjt276");

           contact.setAddress("aksdfj");

           contact.setZipCode("230051");

           contact.setContactTel("3464661");

          

           user.setContact(contact);

           session.save(user);

          

           tx.commit();

實體類中引用值對象時,不用先保存值對象,因爲它不是實體類,它只是一個附屬類,而session.save()中保存的對象是實體類。

 

三、       多對一 –單向

場景:用戶和組;從用戶角度來,多個用戶屬於一個組(多對一 關聯)

使用hibernate開發的思路:先建立對象模型(領域模型),把實體抽取出來。

目前兩個實體:用戶和組兩個實體,多個用戶屬於一個組,那麼一個用戶都會對應於一個組,所以用戶實體中應該有一個持有組的引用。

(一) 對象模型圖:



(二) 關係模型:

 
 

 


(三) 關聯映射的本質:

    將關聯關係映射到數據庫,所謂的關聯關係是對象模型在內存中一個或多個引用。

(四) 實體類

User實體類:

public class User {

  private int id;

  private String name;

  private Group group;

  public Group getGroup() {return group;  }

public void setGroup(Group group) {this.group = group;}

  public int getId() {return id;  }

  public void setId(int id) { this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {  this.name = name;}}

Group實體類:

public class Group {

  private int id;

  private String name;

  public int getId() {return id;}

  public void setId(int id) { this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {this.name = name;}

}

實體類建立完後,開始創建映射文件,先建立簡單的映射文件:

(五) xml方式:映射文件:

1、 Group實體類的映射文件:

<hibernate-mapping>

  <class name="com.wjt276.hibernate.Group" table="t_group">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="name"/>

  </class>

</hibernate-mapping>

2、 User實體類的映射文件:

<hibernate-mapping>

  <class name="com.wjt276.hibernate.User" table="t_user">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="name"/>

      <!--<many-to-one> 關聯映射 多對一的關係

  name:是維護的屬性(User.group),這樣表示在多的一端表裏加入一個字段名稱爲group,

但group與SQL中的關鍵字重複,所以需要重新命名字段(column="groupid").這樣這個字段(groupid)會作爲外鍵參照數據庫中group表(t_group也叫一的一端),也就是就在多的一 端加入一個外鍵指向一的一端。 -->

      <many-to-one name="group" column="groupid"/>

  </class>

</hibernate-mapping>

3、 ※<many-to-one>標籤※:

例如:<many-to-one name="group" column="groupid"/>

<many-to-one> 關聯映射 多對一的關係

name:是維護的屬性(User.group),這樣表示在多的一端表裏加入一個字段名稱爲group,但group與SQL中的關鍵字重複,所以需要重新命名字段(column="groupid").這樣這個字段(groupid)會作爲外鍵參照數據庫中group表(t_group也叫一的一端),也就是就在多的一端加入一個外鍵指向一的一端。

 

         這樣導出至數據庫會生成下列語句:

alter table t_user drop foreign keyFKCB63CCB695B3B5AC

drop table if exists t_group

drop table if exists t_user

create table t_group (id integer not nullauto_increment, name varchar(255), primary key (id))

create table t_user (id integer not nullauto_increment, name varchar(255), groupid integer,primary key (id))

alter table t_user add index FKCB63CCB695B3B5AC (groupid), add constraint FKCB63CCB695B3B5AC foreign key (groupid) referencest_group (id)

 

(六) annotation

Group(一的一端)註解只需要正常的註解就可以了,因爲在實體類中它是被引用的。

     User*(多的一端):@ManyToOne來註解多一對的關鍵,並且用@JoinColumn來指定外鍵的字段名

@Entity

public class User {

  private int id;

  private String name;

  private Group group;

 

  @ManyToOne

  @JoinColumn(name="groupId")

  public Group getGroup() {

      return group;

  }

(七) 多對一 存儲(先存儲group(對象持久化狀態後,再保存user)):

session = HibernateUtils.getSession();

            tx =session.beginTransaction();

 

            Groupgroup = new Group();

            group.setName("wjt276");

            session.save(group); //存儲Group對象。

           

            Useruser1 = new User();

            user1.setName("菜10");

            user1.setGroup(group);//設置用戶所屬的組

           

            Useruser2 = new User();

            user2.setName("容祖兒");

            user2.setGroup(group);//設置用戶所屬的組

           

            //開始存儲

            session.save(user1);//存儲用戶

            session.save(user2);

                       

            tx.commit();//提交事務

執行後hibernate執行以下SQL語句:

Hibernate: insert into t_group (name) values (?)

Hibernate: insert into t_user (name, groupid) values (?, ?)

Hibernate: insert into t_user (name, groupid) values (?, ?)

注意:如果上面的session.save(group)不執行,則存儲不存儲不成功。則拋出TransientObjectException異常。

因爲Group爲Transient狀,Object的id沒有分配值。2610644

 

結果:persistent狀態的對象是不能引用Transient狀態的對象

 

以上代碼操作,必須首先保存group對象,再保存user對象。我們可以利用cascade(級聯)方式,不需要先保存group對象。而是直接保存user對象,這樣就可以在存儲user之前先把group存儲了。

    利用cascade屬性是解決TransientObjectException異常的一種手段。

(八) 重要屬性-cascade(級聯):

    級聯的意思是指定兩個對象之間的操作聯運關係,對一個 對象執行了操作之後,對其指定的級聯對象也需要執行相同的操作,取值:all、none、save_update、delete

1、    all:代碼在所有的情況下都執行級聯操作

2、    none:在所有情況下都不執行級聯操作

3、    save-update:在保存和更新的時候執行級聯操作

4、    delete:在刪除的時候執行級聯操作。

 

1、 xml

        例如:<many-to-one name="group"column="groupid" cascade="save-update"/>

2、 annotation

cascade屬性:其值:  CascadeType.ALL                       所有

CascadeType.MERGE                save+ update

CascadeType.PERSIST              

CascadeType.REFRESH

CascadeType.REMOVE

         例如:

                   @ManyToMany(cascade={CascadeType.ALL})

 

注意:cascade只是幫我們省了編程的麻煩而已,不要把它的作用看的太大

(九) 多對一  加載數據

代碼如下:

session = HibernateUtils.getSession();

            tx =session.beginTransaction();

           

            Useruser = (User)session.load(User.class, 3);

            System.out.println("user.name=" + user.getName());

            System.out.println("user.group.name=" + user.getGroup().getName());

       

            //提交事務

            tx.commit();

執行後向SQL發出以下語句:

Hibernate: select user0_.id as id0_0_,user0_.name as name0_0_, user0_.groupid as groupid0_0_ from t_user user0_ whereuser0_.id=?

Hibernate: select group0_.id as id1_0_,group0_.name as name1_0_ from t_group group0_ where group0_.id=?

 

可以加載Group信息:因爲採用了<many-to-one>這個標籤,這個標籤會在多的一端(User)加一個外鍵,指向一的一端(Group),也就是它維護了從多到一的這種關係,多指向一的關係。當你加載多一端的數據時,它就能把一的這一端數據加載上來。當加載User對象後hibernate會根據User對象中的groupid再來加載Group信息給User對象中的group屬性。

 

四、       一對多 - 單向

在對象模型中,一對多的關聯關係,使用集合來表示。

實例場景:班級對學生;Classes(班級)和Student(學生)之間是一對多的關係。

(一) 對象模型:


(二) 關係模型:

 

 


一對多關聯映射利用了多對一關聯映射原理。

(三) 多對一、一對多的區別:

多對一關聯映射:在多的一端加入一個外鍵指向一的一端,它維護的關係是多指向一的。

一對多關聯映射:在多的一端加入一個外鍵指向一的一端,它維護的關係是一指向多的。

兩者使用的策略是一樣的,只是各自所站的角度不同。

(四) 實體類

Classes實體類:

public class Classes {

  private int id;

  private String name;

  //一對多通常使用Set來映射,Set是不可重複內容。

//注意使用Set這個接口,不要使用HashSet,因爲hibernate有延遲加載,

  private Set<Student>students = new HashSet<Student>();

  public int getId() {return id;  }

  public void setId(int id) {this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {this.name = name;}

}

Student實體類:

public class Student {

  private int id;

  private String name;

  public int getId() {return id;}

  public void setId(int id) { this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {  this.name = name;}

}

(五) xml方式:映射

1、 Student映射文件:

<hibernate-mapping>

    <class name="com.wjt276.hibernate.Student" table="t_student">

        <id name="id" column="id">

            <generator class="native"/>

        </id>

        <property name="name" column="name"/>

    </class>

</hibernate-mapping>

2、 Classes映射文件:

<hibernate-mapping>

    <class name="com.wjt276.hibernate.Classes" table="t_classess">

        <id name="id" column="id">

            <generator class="native"/>

        </id>

        <property name="name" column="name"/>      

        <!--<set>標籤 映射一對多(映射set集合),name="屬性集合名稱",然後在用<key>標籤,在多的一端加入一個外鍵(column屬性指定列名稱)指向一的一端,再採用<one-to-many>標籤說明一對多,還指定<set>標籤中name="students"這個集合中的類型要使用完整的類路徑(例如:class="com.wjt276.hibernate.Student") -->

        <set name="students">

            <key column="classesid"/>

            <one-to-many class="com.wjt276.hibernate.Student"/>

        </set>

    </class>

</hibernate-mapping>

(六) annotateon註解

一對多 多的一端只需要正常註解就可以了。

需要在一的一端進行註解一對多的關係。

使用@OneToMany

@Entity

public class Classes {

    private int id;

    private String name;

   

    // 一對多通常使用Set來映射,Set是不可重複內容。

    // 注意使用Set這個接口,不要使用HashSet,因爲hibernate有延遲加載,

    private Set<Student>students = new HashSet<Student>();

    @OneToMany//進行註解爲一對多的關係

    @JoinColumn(name="classesId")//在多的一端註解一個字段(名爲classessid)

    public Set<Student>getStudents() {

        return students;

}

 

(七) 導出至數據庫(hbmàddl)生成的SQL語句:

create table t_classes (id integer not null auto_increment, namevarchar(255), primary key (id))

create table t_student (id integer not null auto_increment, namevarchar(255), classesid integer, primary key (id))

alter table t_student add index FK4B90757070CFE27A (classesid), add constraint FK4B90757070CFE27A foreign key (classesid) referencest_classes (id)

(八) 一對多 單向存儲實例:

             session = HibernateUtils.getSession();

            tx =session.beginTransaction();

   

            Studentstudent1 = new Student();

            student1.setName("10");

            session.save(student1);//必需先存儲,否則在保存classess時出錯.

           

            Studentstudent2 = new Student();

            student2.setName("祖兒");

            session.save(student2);//必需先存儲,否則在保存classess時出錯.

           

            Set<Student>students = new HashSet<Student>();

            students.add(student1);

            students.add(student2);

           

            Classesclasses = new Classes();

            classes.setName("wjt276");

            classes.setStudents(students);

           

            session.save(classes);

            tx.commit();

(九) 生成的SQL語句:

Hibernate: insert into t_student (name) values (?)

Hibernate: insert into t_student (name) values (?)

Hibernate: insert into t_classes (name) values (?)

Hibernate: update t_student set classesid=? where id=?

Hibernate: update t_student set classesid=? where id=?

 

(十) 一對多,在一的一端維護關係的缺點:

    因爲是在一的一端維護關係,這樣會發出多餘的更新語句,這樣在批量數據時,效率不高。

    還有一個,當在多的一端的那個外鍵設置爲非空時,則在添加多的一端數據時會發生錯誤,數據存儲不成功。

(十一)       一對多 單向數據加載:

session = HibernateUtils.getSession();

          tx =session.beginTransaction();

          Classesclasses = (Classes)session.load(Classes.class, 2);

          System.out.println("classes.name=" + classes.getName());

          Set<Student> students = classes.getStudents();

          for(Iterator<Student> iter = students.iterator();iter.hasNext();){

              Studentstudent = iter.next();

              System.out.println(student.getName());

          }

          tx.commit();

(十二)       加載生成SQL語句:

Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ fromt_classes classes0_ where classes0_.id=?

Hibernate: select students0_.classesid as classesid1_, students0_.id asid1_, students0_.id as id1_0_, students0_.name as name1_0_ from t_studentstudents0_ where students0_.classesid=?

 

五、       一對多 - 雙向

         是加載學生時,能夠把班級加載上來。當然加載班級也可以把學生加載上來

1、  在學生對象模型中,要持有班級的引用,並修改學生映射文件就可以了。。

2、  存儲沒有變化

3、  關係模型也沒有變化

(一) xml方式:映射

學生映射文件修改後的:

  <class name="com.wjt276.hibernate.Student" table="t_student">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="name" column="name"/>

<!--使用多對一標籤映射 一對多雙向,下列的column值必需與多的一端的key字段值一樣。-->

      <many-to-one name="classes" column="classesid"/>

  </class>

 

如果在一對多的映射關係中採用一的一端來維護關係的話會存在以下兩個缺點:①如果多的一端那個外鍵設置爲非空時,則多的一端就存不進數據;②會發出多於的Update語句,這樣會影響效率。所以常用對於一對多的映射關係我們在多的一端維護關係,並讓多的一端維護關係失效(見下面屬性)。

代碼:

  <class name="com.wjt276.hibernate.Classes" table="t_classes">

      <id name="id"column="id">

          <generator class="native"/>

      </id>

      <property name="name"column="name"/>

     

      <!--

          <set>標籤 映射一對多(映射set集合),name="屬性集合名稱"

          然後在用<key>標籤,在多的一端加入一個外鍵(column屬性指定列名稱)指向一的一端

          再採用<one-to-many>標籤說明一對多,還指定<set>標籤中name="students"這個集合中的類型

          要使用完整的類路徑(例如:class="com.wjt276.hibernate.Student")

inverse="false":一的一端維護關係失效(反轉) :false:可以從一的一端維護關係(默認);true:從一的一端維護關係失效,這樣如果在一的一端維護關係則不會發出Update語句。 -->

      <set name="students"inverse="true">

      <key column="classesid"/>

          <one-to-many class="com.wjt276.hibernate.Student"/>

      </set>

  </class>

 

(二) annotateon方式註解

首先需要在多的一端持有一的一端的引用

因爲一對多的雙向關聯,就是多對一的關係,我們一般在多的一端來維護關係,這樣我們需要在多的一端實體類進行映射多對一,並且設置一個字段,而在一的一端只需要進行映射一對多的關係就可以了,如下:

多的一端

@Entity

public class Student {

    private int id;

    private String name;

    private Classes classes;

   

    @ManyToOne

    @JoinColumn(name="classesId")

    public ClassesgetClasses() {

        return classes;

    }

一的一端

@Entity

public class Classes {

    private int id;

    private String name;

    // 一對多通常使用Set來映射,Set是不可重複內容。

    // 注意使用Set這個接口,不要使用HashSet,因爲hibernate有延遲加載,

    private Set<Student>students = new HashSet<Student>();

 

    @OneToMany(mappedBy="classes")//進行註解爲一對多的關係

    public Set<Student>getStudents() {

        return students;}

(三) 數據保存:

一對多 數據保存,從多的一端進行保存:

session = HibernateUtils.getSession();

          tx =session.beginTransaction();

 

          Classesclasses = new Classes();

          classes.setName("wjt168");

          session.save(classes);

         

          Studentstudent1 = new Student();

          student1.setName("10");

          student1.setClasses(classes);

          session.save(student1);

         

          Studentstudent2 = new Student();

          student2.setName("祖兒");

          student2.setClasses(classes);

          session.save(student2);

         

          //提交事務

          tx.commit();

注意:一對多,從多的一端保存數據比從一的一端保存數據要快,因爲從一的一端保存數據時,會多更新多的一端的一個外鍵(是指定一的一端的。)

(四) 關於inverse屬性:

inverse主要用在一對多和多對多雙向關聯上,inverse可以被設置到集合標籤<set>上,默認inverse爲false,所以我們可以從一的一端和多的一端維護關聯關係,如果設置inverse爲true,則我們只能從多的一端維護關聯關係。

    注意:inverse屬性,隻影響數據的存儲,也就是持久化

(五) Inverse和cascade區別:

    Inverse是關聯關係的控制方向

    Casecade操作上的連鎖反應

(六) 一對多雙向關聯映射總結:

    在一的一端的集合上使用<key>,在對方表中加入一個外鍵指向一的一端

    在多的一端採用<many-to-one>

注意:<key>標籤指定的外鍵字段必須和<many-to-one>指定的外鍵字段一致,否則引用字段的錯誤

如果在一的一端維護一對多的關係,hibernate會發出多餘的update語句,所以我們一般在多的一端來維護關係。

 

六、       多對多 - 單向

Ø        一般的設計中,多對多關聯映射,需要一箇中間表

Ø        Hibernate會自動生成中間表

Ø        Hibernate使用many-to-many標籤來表示多對多的關聯

Ø        多對多的關聯映射,在實體類中,跟一對多一樣,也是用集合來表示的。

(一) 實例場景:

用戶與他的角色(一個用戶擁有多個角色,一個角色還可以屬於多個用戶)

(二) 對象模型:



(三) 關係模型:

 
 
 

(四) 實體類

Role實體類:

public class Role {

    private int id;

    private String name;

    public int getId() return id;}

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

    public void setName(Stringname) {  this.name = name;}}

User實體類:

public class User {

    private int id;

    private String name;   

    private Set<User> roles = newHashSet<User>();// Role對象的集合

    public int getId() {return id;}

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

    public void setName(Stringname) {this.name = name;}

    public Set<User>getRoles() {return roles;}

    public voidsetRoles(Set<User> roles) {this.roles = roles;  }

(五) xml方式:映射

Role映射文件:

<class name="com.wjt276.hibernate.Role" table="t_role">

        <id name="id">

            <generator class="native"/>

        </id>

        <property name="name" column="name"/>

    </class>

User映射文件:

    <class name="com.wjt276.hibernate.User" table="t_user">

        <id name="id"column="id">

            <generator class="native"/>

        </id>

        <property name="name"/>

<!--使用<set>標籤映射集合(set),標籤中的name值爲對象屬性名(集合roles),而使用table屬性是用於生成第三方表名稱,例:table="t_user_role",但是第三方面中的字段是自動加入的,作爲外鍵分別指向其它表。

所以表<key>標籤設置,例:<key column="userid"/>,意思是:在第三方表(t_user_role)中加入一個外鍵並且指向當前的映射實體類所對應的表(t_user).使用<many-to-many>來指定此映射集合所對象的類(實例類),並且使用column屬性加入一個外鍵指向Role實體類所對應的表(t_role) -->

        <setname="roles" table="t_user_role">

            <key column="userid"/>

            <many-to-many class="com.wjt276.hibernate.Role" column="roleid"/>

        </set>

    </class>

(六) annotation註解方式

注意:因爲是多對多單向(當然用戶擁有多個角色,一個角色也可屬性多個用戶,但這裏角色看不到用戶),所以角色只需要正常註解就可以了,

現在要使用@ManyToMany來註解多對多的關係,並使用@JoinTabel來註解第三方表的名稱,再使用joinColumns屬性來指定當前對象在第三方表中的字段名,並且這個字段會指向當前類相對應的表,最後再用inverseJoinColumns來指定當前類持有引用的實體類在第三方表中的字段名,並且指向被引用對象相對應的表,如下:

@Entity

public class User {

  private int id;

  private String name;

  private Set<User> roles = new HashSet<User>();// Role對象的集合  @Id

  @GeneratedValue

  public int getId() {return id;}

 

  @ManyToMany

  @JoinTable(name="u_r",//使用@JoinTable標籤的name屬性註解第三方表名稱

          joinColumns={@JoinColumn(name="userId")},

//使用joinColumns屬性來註解當前實體類在第三方表中的字段名稱並指向該對象

          inverseJoinColumns={@JoinColumn(name="roleId")}

//使用inverseJoinColumns屬性來註解當前實體類持有引用對象在第三方表中的字段名稱並指向被引用對象表

  )

  public Set<User> getRoles() {return roles;}

(七) 生成SQL語句

create table t_role (id integer not null auto_increment, namevarchar(255), primary key (id))

create table t_user (id integer not null auto_increment, namevarchar(255), primary key (id))

create table t_user_role (userid integer not null, roleid integer notnull, primary key (userid, roleid))

alter table t_user_role add index FK331DEE5F1FB4B2D4 (roleid), add constraint FK331DEE5F1FB4B2D4 foreign key (roleid) referencest_role (id)

alter table t_user_role add index FK331DEE5F250A083E(userid), add constraint FK331DEE5F250A083E foreign key (userid) referencest_user (id)

注:根據DDL語句可以看出第三方表的主鍵是一個複合主鍵(primary key (userid, roleid)),也就是說記錄不可以有相同的數據。

(八) 數據庫表及結構:


(九) 多對多關聯映射 單向數據存儲:

session = HibernateUtils.getSession();

          tx =session.beginTransaction();

 

          Role r1 = new Role();

          r1.setName("數據錄入人員");

          session.save(r1);

         

          Role r2 = new Role();

          r2.setName("商務主管");

          session.save(r2);

         

          Role r3 = new Role();

          r3.setName("大區經理");

          session.save(r3);

         

          User u1 = new User();

          u1.setName("10");

          Set<Role>u1Roles = new HashSet<Role>();

          u1Roles.add(r1);

          u1Roles.add(r2);

          u1.setRoles(u1Roles);

         

          User u2 = new User();

          u2.setName("祖兒");

          Set<Role>u2Roles = new HashSet<Role>();

          u2Roles.add(r2);

          u2Roles.add(r3);

          u2.setRoles(u2Roles);

         

          User u3 = new User();

          u3.setName("成龍");

          Set<Role>u3Roles = new HashSet<Role>();

          u3Roles.add(r1);

          u3Roles.add(r2);

          u3Roles.add(r3);

          u3.setRoles(u3Roles);

         

          session.save(u1);

          session.save(u2);

          session.save(u3);

         

          tx.commit();

發出SQL語句:

Hibernate: insert into t_role (name) values (?)

Hibernate: insert into t_role (name) values (?)

Hibernate: insert into t_role (name) values (?)

Hibernate: insert into t_user (name) values (?)

Hibernate: insert into t_user (name) values (?)

Hibernate: insert into t_user (name) values (?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

Hibernate: insert into t_user_role (userid, roleid)values (?, ?)

注:前三條SQL語句,添加Role記錄,第三條到第六條添加User,最後7條SQL語句是在向第三方表(t_user_role)中添加多對多關係(User與Role關係)

 

(十) 多對多關聯映射 單向數據加載:

         session =HibernateUtils.getSession();

         tx =session.beginTransaction();

        

         User user =(User)session.load(User.class, 1);

         System.out.println("user.name=" + user.getName());

         for(Iterator<Role> iter = user.getRoles().iterator();iter.hasNext();){

             Rolerole = (Role) iter.next();

             System.out.println(role.getName());

         }

         tx.commit();

生成SQL語句:

Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_useruser0_ where user0_.id=?

user.name=10

Hibernate: select roles0_.userid as userid1_, roles0_.roleid as roleid1_,role1_.id as id2_0_, role1_.name as name2_0_ from t_user_role roles0_ leftouter join t_role role1_ on roles0_.roleid=role1_.id where roles0_.userid=?

商務主管

數據錄入人員

七、       多對多 - 雙向

    多對多關聯映射 雙向 兩方都持有對象引用,修改對象模型,但數據的存儲沒有變化。

(一)     xml方式:映射

再修改映射文件:

    <class name="com.wjt276.hibernate.Role" table="t_role">

        <id name="id">

            <generator class="native"/>

        </id>

        <property name="name" column="name"/>

        <!—order-by 屬性是第三方表哪個字段進行排序-->

        <set name="users" table="t_user_role"order-by="userid">

            <key column="roleid"/>

            <many-to-many class="com.wjt276.hibernate.User" column="userid"/>

        </set>  </class>

注:數據的存儲與單向一樣。但一般維護這個多對多關係,只需要使用一方,而使另一方維護關係失效。

總結:

<!— order-by 屬性是第三方表哪個字段進行排序-->

      <set name="users" table="t_user_role"order-by="userid">

          <key column="roleid"/>

          <many-to-many class="com.wjt276.hibernate.User" column="userid"/>

      </set>

Ø        table屬性值必須和單向關聯中的table屬性值一致

Ø        <key>中column屬性值要與單向關聯中的<many-to-many>標籤中的column屬性值一致

Ø        在<many-to-many>中的column屬性值要與單向關聯中<key>標籤的column屬性值一致。

 

(二) annotation註解方式

多對多關聯映射 雙向 兩方都持有對象引用,修改對象模型,但數據的存儲沒有變化

只需要修改註解映射就可以了。

User實體類註解沒有變化和單向一樣:

@Entity

public class User {

  private int id;

  private Set<User> roles = new HashSet<User>();// Role對象的集合  @Id

  @GeneratedValue

  public int getId() {return id;}

  @ManyToMany

  @JoinTable(name="u_r",//使用@JoinTable標籤的name屬性註解第三方表名稱

          joinColumns={@JoinColumn(name="userId")},//使用joinColumns屬性來註解當前實體類在第三方表中的字段名稱並指向該對象

          inverseJoinColumns={@JoinColumn(name="roleId")}

//使用inverseJoinColumns屬性來註解當前實體類持有引用對象在第三方表中的字段名稱並指向被引用對象表

  )

  public Set<User> getRoles() {return roles;}

 

Role實體類註解也非常的簡單:使用@ManyToMany註解,並使用mappedBy屬性指定引用對象持有自己的的屬性名

@Entity

public class Role {

  private int id;

  private String name;

  private Set<User> users = newHashSet<User>();

  @Id

  @GeneratedValue

  public int getId() {return id;  }

  @ManyToMany(mappedBy="roles")

  public Set<User>getUsers() {return users;}

  public voidsetUsers(Set<User> users) {this.users = users;  }

多對多關聯映射 雙向 數據加載

           session =HibernateUtils.getSession();

           tx =session.beginTransaction();

          

           Role role= (Role)session.load(Role.class, 1);

           System.out.println("role.name=" + role.getName());

           for(Iterator<User> iter = role.getUsers().iterator();iter.hasNext();){

               Useruser = iter.next();

               System.out.println("user.name=" + user.getName());

           }

           tx.commit();

生成SQL語句:

Hibernate: select role0_.id as id2_0_, role0_.name as name2_0_ from t_rolerole0_ where role0_.id=?

role.name=數據錄入人員

Hibernate: select users0_.roleid as roleid1_, users0_.userid as userid1_,user1_.id as id0_0_, user1_.name as name0_0_ from t_user_role users0_ leftouter join t_user user1_ on users0_.userid=user1_.id where users0_.roleid=?order by users0_.userid

user.name=10

user.name=成龍

八、       關聯關係中的CRUD_Cascade_Fetch

1、  設定cascade可以設定在持久化時對於關聯對象的操作(CUD,R歸Fetch管)

2、  cascade僅僅是幫助我們省了編程的麻煩而已,不要把它的作用看的太大

a)        cascade的屬性指明做什麼操作的時候關係對象是綁在一起的

b)        refresh=A裏面需要讀B改過之後的數據

3、  鐵律:雙向關係在程序中要設定雙向關聯

4、  鐵律:雙向一定需要設置mappedBy

5、  fetch

a)        鐵律:雙向不要兩邊設置Eager(會有多餘的查詢語句發出)

b)        對多方設置fetch的時候要謹慎,結合具體應用,一般用Lazy不用eager,特殊情況(多方數量不多的可以考慮,提高效率的時候可以考慮)

6、  O/RMapping編程模型

a)        映射模型

                                    i.  jpa annotation

                                   ii.  hibernate annotation extension

b)        編程接口

                                    i.  jpa

                                   ii.  hibernate

c)         數據查詢語言

                                    i.  HQL

                                   ii.  EJBQL(JPQL)

7、  要想刪除或者更新,先做load,除了精確知道ID之外

8、  如果想消除關聯關係,先設定關係爲null,再刪除對應記錄,如果不刪記錄,該記錄就變成垃圾數據

九、       集合映射

1、 Set

2、 List

a)        @OrderBy

注意:List與Set註解是一樣的,就是把Set更改爲List就可以了

  private List<User>users = new ArrayList<User>();

  @OneToMany(mappedBy="group",

          cascade={CascadeType.ALL}

          )

  @OrderBy("name ASC")//使用@OrderBy註解List中使用哪個字段進行排序,可以組合排序,中間使用逗號分開

  public List<User>getUsers() {  return users;}

  public voidsetUsers(List<User> users) {this.users = users;}

 

3、 Map

a)        @Mapkey

註解:關聯模型中並沒有變化,只是註解發生了變化,而這個變化是給hibernate看的

Map在映射時,因爲Key是唯一的,所以一般可以使用主鍵表示,然後在映射時使用@MapKey來註解使用哪個字段爲key,如下:

private Map<Integer,User> users = new HashMap<Integer, User>();

@OneToMany(mappedBy="group", cascade=CascadeType.ALL)

@MapKey(name="id")//註解使用哪個字段爲key

public Map<Integer,User> getUsers() {  return users;}

public voidsetUsers(Map<Integer, User> users) {this.users = users;}

                   數據加載:

     Session s = sessionFactory.getCurrentSession();

     s.beginTransaction();

     Group g =(Group)s.load(Group.class, 1);

     for(Map.Entry<Integer,User> entry : g.getUsers().entrySet()) {

         System.out.println(entry.getValue().getName());

     }

     s.getTransaction().commit();

 

十、        繼承關聯映射

繼承映射:就是把類的繼承關係映射到數據庫裏(首先正確的存儲,再正確的加載數據)

(一) 繼承關聯映射的分類:

Ø        單表繼承:每棵類繼承樹使用一個表(table per class hierarchy)

Ø        具體表繼承:每個子類一個表(table per subclass)

Ø        類表繼承:每個具體類一個表(table per concrete class)(有一些限制)

Ø        實例環境:動物Animal有三個基本屬性,然後有一個Pig繼承了它並擴展了一個屬性,還有一個Brid也繼承了並且擴展了一個屬性

(二) 對象模型:


(三) 單表繼承SINGLE_TABLE

每棵類繼承樹使用一個表

把所有的屬性都要存儲表中,目前至少需要5個字段,另外需要加入一個標識字段(表示哪個具體的子類)

t_animal

Id

Name

Sex

Weight

Height

Type

1

豬豬

true

100

 

P

2

鳥鳥

false

 

50

B

其中:

           ①、id:表主鍵

           ②、name:動物的姓名,所有的動物都有

           ③、sex:動物的性別,所有的動物都有

           ④、weight:豬(Pig)的重量,只有豬纔有,所以鳥鳥就沒有重量數據

           ⑤、height:鳥(height)的調試,只有鳥纔有,所以豬豬就沒有高度數據

           ⑥、type:表示動物的類型;P表示豬;B表示鳥

1、 實體類

Animal實體類:

public class Animal {

  private int id;

  private String name;   

  private boolean sex;

  public int getId() {return id;  }

  public void setId(int id) { this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {this.name = name;}

  public boolean isSex() {return sex;}

  public void setSex(boolean sex) {this.sex = sex;}  

}

Pig實體類:

public class Pig extends Animal {

  private int weight;

  public int getWeight() {return weight;}

  public void setWeight(int weight) {this.weight = weight;}

}

Bird實體類:

public class Bird extends Animal {

  private int height;

  public int getHeight() {return height;}

  public void setHeight(int height) {this.height = height;}  

}

2、 xml方式:映射

  <class name="Animal" table="t_animal"lazy="false">

      <id name="id">

          <generator class="native"/>

      </id>

      <discriminator column="type" type="string"/>

      <property name="name"/>

      <property name="sex"/>

      <subclass name="Pig" discriminator-value="P">

          <property name="weight"/>

      </subclass>

      <subclass name="Bird" discriminator-value="B">

          <property name="height"/>

      </subclass>

  </class>

 

1、理解如何映射

      因爲類繼承樹肯定是對應多個類,要把多個類的信息存放在一張表中,必須有某種機制來區分哪些記錄是屬於哪個類的。

  這種機制就是,在表中添加一個字段,用這個字段的值來進行區分。用hibernate實現這種策略的時候,有如下步驟:

  父類用普通的<class>標籤定義

  在父類中定義一個discriminator,即指定這個區分的字段的名稱和類型

  如:<discriminator column=”XXX” type=”string”/>

  子類使用<subclass>標籤定義,在定義subclass的時候,需要注意如下幾點:

  Subclass標籤的name屬性是子類的全路徑名

  在Subclass標籤中,用discriminator-value屬性來標明本子類的discriminator字段(用來區分不同類的字段)

  的值Subclass標籤,既可以被class標籤所包含(這種包含關係正是表明了類之間的繼承關係),也可以與class標

  籤平行。 當subclass標籤的定義與class標籤平行的時候,需要在subclass標籤中,添加extends屬性,裏面的值

  是父類的全路徑名稱。子類的其它屬性,像普通類一樣,定義在subclass標籤的內部。

 

2、理解如何存儲

  存儲的時候hibernate會自動將鑑別字段值插入到數據庫中,在加載數據的時候,hibernate能根據這個鑑別值

  正確的加載對象

 

多態查詢:在hibernate加載數據的時候能鑑別出正真的類型(instanceOf)

 

get支持多態查詢

load只有在lazy=false,才支持多態查詢

hql支持多態查詢

3、 annotation註解

父類中註解如下:

 使用@Inheritance註解爲繼承映射,再使用strategy屬性來指定繼承映射的方式

 strategy有三個值:InheritanceType.SINGLE_TABLE           單表繼承

                  InheritanceType.TABLE_PER_CLASS       類表繼承

                  InheritanceType.JOINED            具體表繼承

 再使用@DiscriminatorColumn注意標識字段的字段名,及字段類型

 在類中使用@DiscriminatorValue來註解標識字段的值

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

@DiscriminatorColumn(

name="discriminator",

discriminatorType=DiscriminatorType.STRING)

@DiscriminatorValue("person")

public class Person {private int id;private String name;

 

  @Id

  @GeneratedValue

public int getId() {return id;}

繼承類中註解如下:

    只需要使用@DiscriminatorValue來註解標識字段的值

 

4、 導出後生成SQL語句:

create table t_animal (id integer not null auto_increment, typevarchar(255) not null, name varchar(255), sex bit, weight integer, heightinteger, primary key (id))

5、 數據庫中表結構:



6、 單表繼承數據存儲:

session = HibernateUtils.getSession();

            tx =session.beginTransaction();

   

            Pig pig = new Pig();

            pig.setName("豬豬");

            pig.setSex(true);

            pig.setWeight(100);

            session.save(pig);

           

            Bird bird = new Bird();

            bird.setName("鳥鳥");

            bird.setSex(false);

            bird.setHeight(50);

            session.save(bird);

           

            tx.commit();

存儲執行輸出SQL語句:

Hibernate: insert into t_animal (name, sex, weight, type) values (?, ?, ?,'P')//自動確定無height字段,並且設置標識符爲P

Hibernate: insert into t_animal (name, sex, height, type) values (?, ?, ?,'B') //自動確定無weight字段,並且設置標識符爲B

解釋:hibernate會根據單表繼承映射文件的配置內容,自動在插入數據時哪個子類需要插入哪些字段,並且自動插入標識符字符值(在映射文件中配置了)

 

7、 單表繼承映射數據加載(指定加載子類):

    session =HibernateUtils.getSession();

    tx =session.beginTransaction();

           

    Pig pig =(Pig)session.load(Pig.class, 1);

    System.out.println("pig.name=" + pig.getName());

    System.out.println("pig.weight=" + pig.getWeight());

           

    Bird bird =(Bird)session.load(Bird.class, 2);

    System.out.println("bird.name" + bird.getName());

    System.out.println("bird.height=" + bird.getHeight());

           

    tx.commit();

8、 加載執行輸出SQL語句:

Hibernate: select pig0_.id as id0_0_, pig0_.name as name0_0_, pig0_.sex assex0_0_, pig0_.weight as weight0_0_ from t_animal pig0_ where pig0_.id=? and pig0_.type='P'

//hibernate會根據映射文件自動確定哪些字段(雖然表中有height,但hibernate並不會加載)屬於這個子類,並且確定標識符爲B(已經在配置文件中設置了)

pig.name=豬豬

pig.weight=100

Hibernate: select bird0_.id as id0_0_, bird0_.name as name0_0_, bird0_.sexas sex0_0_, bird0_.height as height0_0_ from t_animal bird0_ where bird0_.id=?and bird0_.type='B'

//hibernate會根據映射文件自動確定哪些字段雖然表中有weight,但hibernate並不會加載屬於這個子類,並且確定標識符爲B(已經在配置文件中設置了)

bird.name=鳥鳥

bird.height=50

9、 單表繼承映射數據加載(指定加載父類):

session = HibernateUtils.getSession();

            tx =session.beginTransaction();

           

            //不會發出SQL,返回一個代理類

            Animal animal =(Animal)session.load(Animal.class, 1);

            //發出SQL語句,並且加載所有字段的數據(因爲使用父類加載對象數據)

            System.out.println("animal.name=" + animal.getName());

            System.out.println("animal.sex=" + animal.isSex());

           

            tx.commit();

加載執行生成SQL語句:

Hibernate: select animal0_.id as id0_0_, animal0_.name as name0_0_,animal0_.sex as sex0_0_, animal0_.weight asweight0_0_, animal0_.height as height0_0_, animal0_.type as type0_0_ from t_animal animal0_ where animal0_.id=?

//注:因爲使用父類加載數據,所以hibernate會將所有字段(height、weight、type)的數據全部加載,並且條件中沒有識別字段type(也就不區分什麼子類,把所有子類全部加載上來。)

10、 單表繼承映射數據加載(指定加載父類,看能否鑑別真實對象):

            session = HibernateUtils.getSession();

            tx =session.beginTransaction();

           

            //不會發出SQL語句(load默認支持延遲加載(lazy)),返回一個animal的代理對象(此代理類是繼承Animal生成的,也就是說是Animal一個子類)

            Animal animal =(Animal)session.load(Animal.class, 1);

           

            //因爲在上面返回的是一個代理類(父類的一個子類),所以animal不是Pig

            //通過instanceof是反應不出正直的對象類型的,因此load在默認情況下是不支持多態查詢的。

            if (animal instanceof Pig) {

                System.out.println("是豬");

            } else {

                System.out.println("不是豬");//這就是結果

            }

            System.out.println("animal.name=" + animal.getName());

            System.out.println("animal.sex=" + animal.isSex());

           

            tx.commit();

11、 多態查詢:

在hibernate加載數據的時候能鑑別出正直的類型(通過instanceof)

get支持多態查詢;load只有在lazy=false,才支持多態查詢;HQL支持多態查詢

12、 採用load,通過Animal查詢,將<class>標籤上的lazy設置爲false

session = HibernateUtils.getSession();

            tx = session.beginTransaction();

           

            //會發出SQL語句,因爲設置lazy爲false,不支持延遲加載

            Animal animal =(Animal)session.load(Animal.class, 1);

            //可以正確的判斷出Pig的類型,因爲lazy=false,返回具體的Pid類型

            //此時load支持多態查詢

            if (animal instanceof Pig) {

                System.out.println("是豬");//結果

            } else {

                System.out.println("不是豬");

            }

            System.out.println("animal.name=" + animal.getName());

            System.out.println("animal.sex=" + animal.isSex());

           

            tx.commit();

13、 採用get,通過Animal查詢,可以判斷出正直的類型

            session = HibernateUtils.getSession();

            tx = session.beginTransaction();

           

            //會發出SQL語句,因爲get不支持延遲加載,返回的是正直的類型,

            Animal animal =(Animal)session.load(Animal.class, 1);

 

            //可以判斷出正直的類型

            //get是支持多態查詢

            if (animal instanceof Pig) {

                System.out.println("是豬");//結果

            } else {

                System.out.println("不是豬");

            }

            System.out.println("animal.name=" + animal.getName());

            System.out.println("animal.sex=" + animal.isSex());

           

            tx.commit();

14、 採用HQL查詢,HQL是否支持多態查詢

List animalList = session.createQuery("from Animal").list();

            for (Iteratoriter = animalList.iterator(); iter.hasNext();) {

                Animal a = (Animal)iter.next();

                //能夠正確鑑別出正直的類型,HQL是支持多態查詢的。

                if (a instanceof Pig) {

                    System.out.println("是Pig");

                } else if (a instanceof Bird) {

                    System.out.println("是Bird");

                }

            }

 

15、 通過HQL查詢表中所有的實體對象

* HQL語句:session.createQuery("fromjava.lang.Object").list();

    * 因爲所有對象都是繼承Object類

List list = session.createQuery("from java.lang.Object").list();

            for (Iteratoriter = list.iterator(); iter.hasNext();){

                Object o =iter.next();

                if (o instanceof Pig) {

                    System.out.println("是Pig");

                } else {

                    System.out.println("是Bird");

                }

            }

 

 

(四) 具體表繼承JOINED

每個類映射成一個表(table per subclass)

對象模型不用變化,存儲模型需要變化


1、 關係模型:

每個類映射成一個表(table per subclass)



注:因爲需要每個類都映射成一張表,所以Animal也映射成一張表(t_animal),表中字段爲實體類屬性

而pig子類也需要映射成一張表(t_pid),但爲了與父類聯繫需要加入一個外鍵(pidid)指向父類映射成的表(t_animal),字段爲子類的擴展屬性。Bird子類同樣也映射成一張表(t_bird),也加入一個外鍵(birdid)指向父類映射成的表(t_animal),字段爲子類的擴展屬性。

2、 xml方式(每個類映射成一個表):

    <class name="com.wjt276.hibernate.Animal" table="t_animal">

        <id name="id"column="id"><!-- 映射主鍵 -->

            <generator class="native"/>

        </id>

        <property name="name"/><!-- 映射普通屬性 -->

        <property name="sex"/>

        <!--<joined-subclass>標籤:繼承映射 每個類映射成一個表 -->

        <joined-subclass name="com.wjt276.hibernate.Pig" table="t_pig">

<!-- <key>標籤:會在相應的表(當前映射的表)裏,加入一個外鍵 , 參照指向當前類的父類(當前Class標籤對象的表(t_animal))-->

            <key column="pigid"/>

            <property name="weight"/>

        </joined-subclass>

        <joined-subclass name="com.wjt276.hibernate.Bird" table="t_bird">

            <key column="birdid"/>

            <property name="height"/>

        </joined-subclass>

    </class>

1、理解如何映射

        這種策略是使用joined-subclass標籤來定義子類的。父類、子類,每個類都對應一張數據庫表。

    在父類對應的數據庫表中,實際上會存儲所有的記錄,包括父類和子類的記錄;在子類對應的數據庫表中,

    這個表只定義了子類中所特有的屬性映射的字段。子類與父類,通過相同的主鍵值來關聯。實現這種策略的時候,

    有如下步驟:

    父類用普通的<class>標籤定義即可

    父類不再需要定義discriminator字段

    子類用<joined-subclass>標籤定義,在定義joined-subclass的時候,需要注意如下幾點:

    Joined-subclass標籤的name屬性是子類的全路徑名

    Joined-subclass標籤需要包含一個key標籤,這個標籤指定了子類和父類之間是通過哪個字段來關聯的。

    如:<key column=”PARENT_KEY_ID”/>,這裏的column,實際上就是父類的主鍵對應的映射字段名稱。

    Joined-subclass標籤,既可以被class標籤所包含(這種包含關係正是表明了類之間的繼承關係),

    也可以與class標籤平行。 當Joined-subclass標籤的定義與class標籤平行的時候,需要在Joined-subclass

    標籤中,添加extends屬性,裏面的值是父類的全路徑名稱。子類的其它屬性,像普通類一樣,定義在joined-subclass標籤的內部。

3、 annotation註解

因爲,子類生成的表需要引用父類生成的表,所以只需要在父類設置具體表繼承映射就可以了,其它子類只需要使用@Entity註解就可以了

@Entity

@Inheritance(strategy=InheritanceType.JOINED)

 

public class Person {

    private int id;

    private String name;

    @Id

    @GeneratedValue

    public int getId() {return id;}

4、 導出輸出SQL語句:

create table t_animal (id integer notnull auto_increment, name varchar(255), sex bit, primary key (id))

create table t_bird (birdid integernot null, height integer, primary key (birdid))

create table t_pig (pigid integer notnull, weight integer, primary key (pigid))

altertable t_bird add index FKCB5B05A4A554009D(birdid), add constraint FKCB5B05A4A554009D foreign key (birdid) referencest_animal (id)

alter table t_pig add index FK68F8743FE77AC32 (pigid), add constraint FK68F8743FE77AC32 foreign key (pigid) references t_animal (id)

//共生成三個表,並在子類表中各有一個外鍵參照指向父類表

 

數據的存儲,不需要其它的任務變化,直接使用單表繼承存儲就可以了,加載也是一樣。

具體表繼承效率沒有單表繼承高,但是單表繼承會出現多餘的庸於字段,具體表層次分明

 

(五) 類表繼承TABLE_PER_CLASS

每個具體類映射成一個表(table per concreteclass)(有一些限制)

 

對象模型不用變化,存儲模型需要變化


1、 關係模型:

每個具體類(Pig、Brid)映射成一個表(table per concrete class)(有一些限制)

t_pig

Id

Name

Sex

Weight

1

豬豬

True

100

t_bird

Id

Name

Sex

Height

2

鳥鳥

False

50

2、 xml方式:映射文件:

    <class name="com.wjt276.hibernate.Animal" table="t_animal">

        <id name="id"column="id"><!-- 映射主鍵 -->

            <generator class="assigned"/><!-- 每個具體類映射一個表主鍵生成策略不可使用native -->       </id>   

        <property name="name"/><!-- 映射普通屬性 -->

        <property name="sex"/>

        <!--使用<union-subclass>標籤來映射"每個具體類映射成一張表"的映射關係

            ,實現上上面的表t_animal雖然映射到數據庫中,但它沒有任何作用。      -->

        <union-subclass name="com.wjt276.hibernate.Pig" table="t_pig">

            <property name="weight"/>

        </union-subclass>

        <union-subclass name="com.wjt276.hibernate.Bird" table="t_bird">

            <property name="height"/>

        </union-subclass>

    </class>

理解如何映射

    這種策略是使用union-subclass標籤來定義子類的。每個子類對應一張表,而且這個表的信息是完備的,

    即包含了所有從父類繼承下來的屬性映射的字段(這就是它跟joined-subclass的不同之處,

    joined-subclass定義的子類的表,只包含子類特有屬性映射的字段)。實現這種策略的時候,有如下步驟:

    父類用普通<class>標籤定義即可

    子類用<union-subclass>標籤定義,在定義union-subclass的時候,需要注意如下幾點:

    Union-subclass標籤不再需要包含key標籤(與joined-subclass不同)

    Union-subclass標籤,既可以被class標籤所包含(這種包含關係正是表明了類之間的繼承關係),

    也可以與class標籤平行。 當Union-subclass標籤的定義與class標籤平行的時候,需要在Union-subclass

    標籤中,添加extends屬性,裏面的值是父類的全路徑名稱。子類的其它屬性,像普通類一樣,

    定義在Union-subclass標籤的內部。這個時候,雖然在union-subclass裏面定義的只有子類的屬性,

    但是因爲它繼承了父類,所以,不需要定義其它的屬性,在映射到數據庫表的時候,依然包含了父類的所

    有屬性的映射字段。

    注意:在保存對象的時候id是不能重複的(不能使用自增生成主鍵)

3、 annotation註解

只需要對父類進行註解就可以了,

因爲子類表的ID是不可以重複,所以一般的主鍵生成策略已經不適應了,只有表主鍵生成策略。

首先使用@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)來註解繼承映射,並且使用具體表繼承方式,使用@TableGenerator來申明一個表主鍵生成策略

再在主鍵上@GeneratedValue(generator="t_gen", strategy=GenerationType.TABLE)來註解生成策略爲表生成策略,並且指定表生成策略的名稱

繼承類只需要使用@Entity進行註解就可以了

@Entity

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

@TableGenerator(

      name="t_gen",

      table="t_gen_table",

      pkColumnName="t_pk",

      valueColumnName="t_value",

      pkColumnValue="person_pk",

      initialValue=1,

      allocationSize=1

      )

public class Person {

private int id;

private String name;

 

  @Id

  @GeneratedValue(generator="t_gen", strategy=GenerationType.TABLE)

  public int getId() {return id;}

 

 

4、 導出輸出SQL語句:

create table t_animal (id integer not null, name varchar(255), sex bit, primary key (id))

create table t_bird (id integer not null, name varchar(255), sex bit, height integer, primary key(id))

create table t_pig (id integer not null, name varchar(255), sex bit, weight integer, primary key(id))

注:表t_animal、 t_bird、t_pig並不是自增的,是因爲bird、pig都是animal類,也就是說animal不可以有相同的ID號(Bird、Pig是類型,只是存儲的位置不同而以)

5、 數據庫表結構如下:



注:如果不想t_animal存在(因爲它沒有實際的作用),可以設置<class>標籤中的abstract="true"(抽象表),這樣在導出至數據庫時,就不會生成t_animal表了。

<class name="com.wjt276.hibernate.Animal" table="t_animal" abstract="true">

        <id name="id"column="id"><!-- 映射主鍵 -->

…………

(六) 三種繼承關聯映射的區別:

1、  第一種:它把所有的數據都存入一個表中,優點:效率好(操作的就是一個表);缺點:存在庸於字段,如果將庸於字段設置爲非空,則就無法存入數據;

2、  第二種:層次分明,缺點:效率不好(表間存在關聯表)

3、  第三種:主鍵字段不可以設置爲自增主鍵生成策略。

 

一般使用第一種

 

第17課 hibernate樹形結構(重點)

         樹形結構:也就是目錄結構,有父目錄、子目錄、文件等信息,而在程序中樹形結構只是稱爲節點。

         一棵樹有一個根節點,而根節點也有一個或多個子節點,而一個子節點有且僅有一個父節點(當前除根節點外),而且也存在一個或多個子節點。

         也就是說樹形結構,重點就是節點,也就是我們需要關心的節點對象。

         節點:一個節點有一個ID、一個名稱、它所屬的父節點(根節點無父節點或爲null),有一個或多的子節點等其它信息。

Hibernate將節點抽取出成實體類,節點相對於父節點是“多對一”映射關係,節點相對於子節點是“一對多”映射關係。

一、  節點實體類:

/** * 節點*/

public class Node {

    private int id; //標識符

    private String name; //節點名稱 

    private int level; //層次,爲了輸出設計   

    private boolean leaf; //是否爲葉子節點,這是爲了效率設計,可有可無

    //父節點:因爲多個節點屬於一個父節點,因此用hibernate映射關係說是“多對一”

    private Node parent;

    //子節點:因爲一個節點有多個子節點,因此用hibernate映射關係說是“一對多”

    private Set children;

 

    public int getId() {return id;}

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

    public void setName(Stringname) {  this.name = name;}

    public int getLevel() { return level;}

    public void setLevel(int level) {this.level = level;}

    public boolean isLeaf() {return leaf;}

    public void setLeaf(boolean leaf) {this.leaf = leaf;}

    public Node getParent() {return parent;}

    public void setParent(Nodeparent) {this.parent = parent;}

    public SetgetChildren() {return children;}

    public void setChildren(Setchildren) {this.children = children;}}

二、  xml方式:映射文件:

  <class name="com.wjt276.hibernate.Node" table="t_node">

      <id name="id" column="id">

          <generator class="native"/>

      </id>

      <property name="name"/>

      <property name="level"/>

      <property name="leaf"/>

<!— 一對多:加入一個外鍵,參照當前表t_node主鍵,    而屬性parent類型爲Node,也就是當前類,則會在同一個表中加入這個字段,參照這個表的主鍵-->

      <many-to-one name="parent" column="pid"/>

  <!--<set>標籤是映射一對多的方式,加入一個外鍵,參照主鍵。-->

      <set name="children" lazy="extra"inverse="true">

          <key column="pid"/>

          <one-to-many class="com.wjt276.hibernate.Node"/>

      </set>     

  </class>

三、  annotation註解

因爲樹型節點所有的數據,在數據庫中只是存儲在一個表中,而對於實體類來說,節點對子節點來說是一對多的關係,而對於父節點來說是多對一的關係。因此可以在一個實體類中註解。如下

@Entity

public class Node {

    private int id; // 標識符

    private String name; // 節點名稱

    private int level; // 層次,爲了輸出設計

    private boolean leaf; // 是否爲葉子節點,這是爲了效率設計,可有可無

    // 父節點:因爲多個節點屬於一個父節點,因此用hibernate映射關係說是“多對一”

    private Node parent;

    // 子節點:因爲一個節點有多個子節點,因此用hibernate映射關係說是“一對多”

    private Set<Node> children = newHashSet<Node>();

    @Id

    @GeneratedValue

    public int getId() {return id;}

    @OneToMany(mappedBy="parent")

    public Set<Node>getChildren() {return children;}

    @ManyToOne

    @JoinColumn(name="pid")

    public Node getParent() {return parent;}

 

四、  測試代碼:

public class NodeTest extends TestCase {

  //測試節點的存在

  public void testSave1(){

      NodeManage.getInstanse().createNode("F:\\hibernate\\hibernate_training_tree");

  }

  //測試節點的加載

  public void testPrintById(){

      NodeManage.getInstanse().printNodeById(1);

  }}

五、  相應的類代碼:

public classNodeManage {  

    private static NodeManage nodeManage= newNodeManage();

    private NodeManage(){}//因爲要使用單例,所以將其構造方法私有化

    //向外提供一個接口

    public static NodeManage getInstanse(){

        return nodeManage;

    }

    /**

     * 創建樹

     *@param filePath 需要創建樹目錄的根目錄

     */

    public void createNode(String dir) {

        Session session = null;    

        try {

            session =HibernateUtils.getSession();

            session.beginTransaction();

           

            File root = new File(dir);

            //因爲第一個節點無父節點,因爲是null

            this.saveNode(root, session, null,0);

           

            session.getTransaction().commit();

        } catch (HibernateException e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        } finally {

            HibernateUtils.closeSession(session);

        }}

    /**

     * 保存節點對象至數據庫

     *@param file 節點所對應的文件

     *@param session session

     *@param parent 父節點

     *@param level 級別

     */

    public void saveNode(File file, Sessionsession, Node parent, int level) {

        if (file == null ||!file.exists()){return;}

        //如果是文件則返回true,則表示是葉子節點,否則爲目錄,非葉子節點

        boolean isLeaf = file.isFile();

        Node node = new Node();

        node.setName(file.getName());

        node.setLeaf(isLeaf);

        node.setLevel(level);

        node.setParent(parent);

       

        session.save(node);

       

        //進行循環迭代子目錄

        File[] subFiles = file.listFiles();

        if (subFiles != null &&subFiles.length > 0){

            for (int i = 0; i <subFiles.length ; i++){

                this.saveNode(subFiles[i],session, node, level + 1);

            }}}

    /**

     * 輸出樹結構

     *@param id

     */

    public void printNodeById(int id) {

 

        Session session = null;

       

        try {

            session =HibernateUtils.getSession();

            session.beginTransaction();

           

            Node node =(Node)session.get(Node.class, 1);

           

            printNode(node);

           

            session.getTransaction().commit();

        } catch (HibernateException e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        } finally {

            HibernateUtils.closeSession(session);

        }      

    }  

    private void printNode(Node node) {    

        if (node == null){  return; }

        int level = node.getLevel();

        if (level > 0){

            for (int i = 0; i < level; i++){

                System.out.print("  |");

            }

            System.out.print("--");            

        }

        System.out.println(node.getName() +(node.isLeaf() ? "" : "[" + node.getChildren().size() +"]"));

        Set children = node.getChildren();

        for (Iterator iter =children.iterator(); iter.hasNext(); ){

            Node child = (Node)iter.next();

            printNode(child);

        }}}


第18課 作業-學生、課程、分數的映射關係

一、        設計

1、  實體類(表)

2、  導航(編程方便)

a)        通過學生 取出 學生所先的課程

b)        但是通過課程 取出 學該課程的 學生不好。學的學生太多

c)         確定編程的方式

3、  可以利用聯合主鍵映射可以,

a)        學生生成一個表

b)        課程生成一個表

c)         再生成一個表,主鍵是聯合主鍵(學生ID、課程ID) + 學生共生成一個表

4、  也可以利用一對多,多對多 都可以(推薦)

a)        學生生成一個表

b)        課程生成一個表

c)         分數生成一個表,並且有兩個外鍵,分別指向學生、課程表

二、        代碼:

* 課程

@Entity

public class Course {

  private int id;

  private String name;

  @Id

  @GeneratedValue

  public int getId() {return id;}

  public void setId(int id) { this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {this.name = name;}}

 

* 分數

@Entity

@Table(name = "score")

public class Score {

  private int id;

  private int score;

  private Student student;

  private Course course;

  @Id

  @GeneratedValue

  public int getId() {return id;}

  @ManyToOne

  @JoinColumn(name = "student_id")

  public StudentgetStudent() {return student;}

  @ManyToOne

  @JoinColumn(name = "score_id")

  public Course getCourse(){ return course;}

  public int getScore() { return score;}

  public void setScore(int score) {this.score = score;}

  public void setStudent(Studentstudent) {this.student = student;}

  public void setCourse(Coursecourse) {this.course = course;}

  public void setId(int id) { this.id = id;}}

 

* 學生通過課程可以導航到分數

@Entity

public class Student {

  private int id;

  private String name;

  private Set<Course> courses = newHashSet<Course>();

  @Id

  @GeneratedValue

  public int getId() {return id;}

  @ManyToMany

  @JoinTable(name = "score", //此表就是Score實體類在數據庫生成的表叫score

          joinColumns= @JoinColumn(name = "student_id"),

          inverseJoinColumns= @JoinColumn(name = "course_id")

      )

  public Set<Course>getCourses() {return courses;}

  public voidsetCourses(Set<Course> courses) {this.courses = courses;}

  public void setId(int id) { this.id = id;}

  public String getName() {return name;}

  public void setName(Stringname) {this.name = name;}}

 

三、        注意

在Student實體類中的使用的第三方表使用了兩個字段,而hibernate會使這兩個字段生成聯合主鍵,這並不是我們需要的結果,因爲我們需要手動到數據庫中修改。這樣纔可以存儲數據,否則數據存儲不進去。這可能是hibernate的一個小bug

 

第19課 Hibernate查詢(Query Language)

HQL VS EJBQL

一、        Hibernate可以使用的查詢語言

1、  NativeSQL:本地語言(數據庫自己的SQL語句)

2、  HQL :Hibernate自帶的查詢語句,可以使用HQL語言,轉換成具體的方言

3、  EJBQL:JPQL 1.0,可以認爲是HQL的一個子節(重點)

4、  QBC:Query By Cretira

5、  QBE:Query By Example

注意:上面的功能是從1至5的比較,1的功能最大,5的功能最小

 

二、        實例一

1、  版塊

/** 版塊*/

@Entity

public class Category {

    private int id;

    private String name;

    @Id

    @GeneratedValue

    public int getId() {return id;}

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

    public void setName(Stringname) {this.name = name;}}

 

2、  主題

/**主題*/

@Entity

public class Topic {

    private int id;

    private String title;

    private Category category;

    //private Category category2;

   

    private Date createDate;   

    public DategetCreateDate() {return createDate;}

    public void setCreateDate(DatecreateDate) {this.createDate = createDate;}

    @ManyToOne(fetch=FetchType.LAZY)

    public CategorygetCategory() { return category;}

    public voidsetCategory(Category category) {this.category = category;   }

    @Id

    @GeneratedValue

    public int getId() {return id;}

    public void setId(int id) {this.id = id;}

    public String getTitle(){return title;}

    public void setTitle(Stringtitle) {this.title = title;}}

3、  主題回覆

/**主題回覆*/

@Entity

public class Msg {

    private int id;

    private String cont;

    private Topic topic;   

    @ManyToOne

    public Topic getTopic() {return topic;}

    public void setTopic(Topictopic) {this.topic = topic;}

    @Id

    @GeneratedValue

    public int getId() {return id;}

    public void setId(int id) {this.id = id;} 

    public String getCont() {return cont;}

    public void setCont(Stringcont) {this.cont = cont;}}

 

4、  臨時類

/**臨時類 */

public class MsgInfo { //VO DTO Value Object username p1 p2UserInfo->User->DB

    private int id;

    private String cont;

    private String topicName;

    private String categoryName;

    public MsgInfo(int id, String cont,String topicName, String categoryName) {

        super();

        this.id = id;

        this.cont = cont;

        this.topicName = topicName;

        this.categoryName =categoryName;

    }

    public StringgetTopicName() {return topicName;}

    public voidsetTopicName(String topicName) {this.topicName = topicName;}

    public StringgetCategoryName() {return categoryName;}

    public voidsetCategoryName(String categoryName) {

        this.categoryName =categoryName;

    }

    public int getId() {return id;}

    public void setId(int id) {this.id = id;} 

    public String getCont() {return cont;}

    public void setCont(Stringcont) {this.cont = cont;}}

三、        實體一測試代碼:

//初始化數據

    @Test

    public void testSave() {

        Sessionsession = sf.openSession();

        session.beginTransaction();    

        for(int i=0; i<10; i++){

            Categoryc = new Category();

            c.setName("c" + i);

            session.save(c);

        }      

        for(int i=0; i<10; i++){

            Categoryc = new Category();

            c.setId(1);

            Topic t= new Topic();

            t.setCategory(c);

            t.setTitle("t" + i);

            t.setCreateDate(new Date());

            session.save(t);           

        }      

        for(int i=0; i<10; i++){          

            Topic t= new Topic();

            t.setId(1);

            Msg m = new Msg();

            m.setCont("m" + i);

            m.setTopic(t);

            session.save(m);           

        }

        session.getTransaction().commit();

        session.close();

    }

 

        /** QL:from + 實體類名稱 */

        Query q =session.createQuery("from Category");

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getName());

        }

       

   

        /* 可以爲實體類起個別名,然後使用它 */

        Query q =session.createQuery("from Category c wherec.name > 'c5'");

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getName());

        }

   

        //排序

        Query q =session.createQuery("from Category c orderby c.name desc");

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getName());

        }

   

         * 爲加載上來的對象屬性起別名,還可以使用

        Query q =session.createQuery("select distinct c fromCategory c order by c.name desc");

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getName());

        }

   

        /*Query q = session.createQuery("from Category c where c.id > :minand c.id < :max");

        //q.setParameter("min",2);

        //q.setParameter("max",8);

        q.setInteger("min",2);

        q.setInteger("max",8);*/

         * 可以使用冒號(:),作爲佔位符,來接受參數使用。如下(鏈式編程)

        Query q =session.createQuery("from Category c wherec.id > :min and c.id < :max")

            .setInteger("min", 2)

            .setInteger("max", 8);

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getId()+ "-" + c.getName());

        }

   

        Query q =session.createQuery("from Category c wherec.id > ? and c.id < ?");

        q.setParameter(0, 2)

            .setParameter(1, 8);

//      q.setParameter(1, 8);

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getId()+ "-" + c.getName());

        }

   

//分頁

        Query q =session.createQuery("from Category c orderby c.name desc");

        q.setMaxResults(4);//每頁顯示的最大記錄數

        q.setFirstResult(2);//從第幾條開始顯示,從0開始

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getId()+ "-" + c.getName());

        }

   

        Query q =session.createQuery("select c.id,  c.name from Category c order by c.namedesc");

        List<Object[]>categories = (List<Object[]>)q.list();

        for(Object[] o : categories) {

            System.out.println(o[0] + "-" + o[1]);

        }

   

    //設定fetch type 爲lazy後將不會有第二條sql語句

        Query q =session.createQuery("from Topic t wheret.category.id = 1");

        List<Topic>topics = (List<Topic>)q.list();

        for(Topic t : topics) {

            System.out.println(t.getTitle());

            //System.out.println(t.getCategory().getName());

        }

   

    //設定fetch type 爲lazy後將不會有第二條sql語句

        Query q =session.createQuery("from Topic t wheret.category.id = 1");

        List<Topic>topics = (List<Topic>)q.list();

        for(Topic t : topics) {

            System.out.println(t.getTitle());

        }

   

        Query q =session.createQuery("from Msg m wherem.topic.category.id = 1");

        for(Object o : q.list()) {

            Msg m = (Msg)o;

            System.out.println(m.getCont());

        }

   

//瞭解即可

    //VO Value Object

    //DTO data transfer object

        Query q =session.createQuery("select newcom.bjsxt.hibernate.MsgInfo(m.id, m.cont, m.topic.title, m.topic.category.name)from Msg");

        for(Object o : q.list()) {

            MsgInfo m = (MsgInfo)o;

            System.out.println(m.getCont());

        }

   

    //動手測試left right join

    //爲什麼不能直接寫Category名,而必須寫t.category

    //因爲有可能存在多個成員變量(同一個類),需要指明用哪一個成員變量的連接條件來做連接

        Query q =session.createQuery("select t.title, c.namefrom Topic t join t.category c "); //join Category c

        for(Object o : q.list()) {

            Object[] m = (Object[])o;

            System.out.println(m[0] + "-" + m[1]);

        }

   

    //學習使用uniqueResult

        Query q = session.createQuery("from Msg m where m = :MsgToSearch "); //不重要

        Msg m = new Msg();

        m.setId(1);

        q.setParameter("MsgToSearch", m);

        Msg mResult =(Msg)q.uniqueResult();

        System.out.println(mResult.getCont());

   

        Query q =session.createQuery("select count(*) fromMsg m");

        long count = (Long)q.uniqueResult();

        System.out.println(count);

   

    Query q = session.createQuery("select max(m.id), min(m.id), avg(m.id), sum(m.id)from Msg m");

        Object[] o =(Object[])q.uniqueResult();

        System.out.println(o[0] + "-" + o[1] + "-" + o[2] + "-" + o[3]);

   

        Query q =session.createQuery("from Msg m where m.idbetween 3 and 5");

        for(Object o : q.list()) {

            Msg m = (Msg)o;

            System.out.println(m.getId()+ "-" + m.getCont());

        }

   

        Query q =session.createQuery("from Msg m where m.idin (3,4, 5)");

        for(Object o : q.list()) {

            Msg m = (Msg)o;

            System.out.println(m.getId()+ "-" + m.getCont());

        }

   

 

    //is null 與 is notnull

        Query q =session.createQuery("from Msg m where m.contis not null");

        for(Object o : q.list()) {

            Msg m = (Msg)o;

            System.out.println(m.getId()+ "-" + m.getCont());

        }

   

四、        實例二

注意:實體二,實體類,只是在實體一的基礎上修改了Topic類,添加了多對一的關聯關係

@Entity

@NamedQueries({

            @NamedQuery(name="topic.selectCertainTopic", query="from Topic t where t.id = :id")

        })

        /*@NamedNativeQueries(

        {

    @NamedNativeQuery(name="topic.select2_5Topic",query="select * from topic limit 2, 5")

        })*/

public class Topic {

    private int id;

    private String title;

    private Category category;

    private Date createDate;

    private List<Msg> msgs = newArrayList<Msg>();

    @OneToMany(mappedBy="topic")

    public List<Msg> getMsgs() {return msgs;}

    public void setMsgs(List<Msg> msgs) {this.msgs = msgs;}

    public Date getCreateDate() {return createDate;}

    public void setCreateDate(Date createDate) {this.createDate = createDate;   }

    @ManyToOne(fetch=FetchType.LAZY)

    public Category getCategory() { return category;}

    public void setCategory(Category category) {this.category = category;}

    @Id

    @GeneratedValue

    public int getId() {return id;}

    public void setId(int id) {this.id = id;}

    public String getTitle() {  return title;}

    public void setTitle(String title) {this.title = title;}}

 

 

五、        實例二測試代碼

注意:測試數據是實例一的測試數據 

    //is empty and is not empty

        Query q =session.createQuery("from Topic t wheret.msgs is empty");

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getId()+ "-" + t.getTitle());

        }

   

        Query q =session.createQuery("from Topic t wheret.title like '%5'");

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getId()+ "-" + t.getTitle());

        }

   

        Query q =session.createQuery("from Topic t wheret.title like '_5'");

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getId()+ "-" + t.getTitle());

        }

 

    //不重要

        Query q = session.createQuery("select lower(t.title)," +

                                             "upper(t.title)," +

                                             "trim(t.title)," +

                                             "concat(t.title,'***')," +

                                             "length(t.title)" +

                                             " from Topict ");

        for(Object o : q.list()) {

            Object[] arr = (Object[])o;

    System.out.println(arr[0] + "-" + arr[1] + "-" + arr[2] + "-" + arr[3] + "-" + arr[4] + "-");

        }

   

        Query q =session.createQuery("select abs(t.id)," +

                                             "sqrt(t.id)," +

                                             "mod(t.id,2)" +

                                             " from Topict ");

        for(Object o : q.list()) {

            Object[] arr =(Object[])o;

            System.out.println(arr[0] + "-" + arr[1] + "-" + arr[2] );

        }

   

Query q = session.createQuery("selectcurrent_date, current_time, current_timestamp, t.id from Topic t");

        for(Object o : q.list()) {

            Object[] arr =(Object[])o;

            System.out.println(arr[0] + " | " + arr[1] + " | " + arr[2] + " | " + arr[3]);

        }

   

        Query q =session.createQuery("from Topic t wheret.createDate < :date");

        q.setParameter("date", new Date());

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getTitle());

        }

   

        Query q =session.createQuery("select t.title,count(*) from Topic t group by t.title") ;

        for(Object o : q.list()) {

            Object[] arr =(Object[])o;

            System.out.println(arr[0] + "|" + arr[1]);

        }

   

Query q = session.createQuery("selectt.title, count(*) from Topic t group by t.title having count(*) >= 1") ;

        for(Object o : q.list()) {

            Object[] arr =(Object[])o;

            System.out.println(arr[0] + "|" + arr[1]);

        }

   

        Query q =session.createQuery("from Topic t where t.id< (select avg(t.id) from Topic t)") ;

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getTitle());

        }

   

        Query q =session.createQuery("from Topic t where t.id< ALL (select t.id from Topic t where mod(t.id, 2)= 0) ") ;

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getTitle());

        }

   

    //用in 可以實現exists的功能

    //但是exists執行效率高

// t.id not in (1)

        Query q =session.createQuery("from Topic t where notexists (select m.id from Msg m where m.topic.id=t.id)") ;

//      Query q =session.createQuery("from Topic t where exists (select m.id from Msg mwhere m.topic.id=t.id)") ;

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getTitle());

        }

   

    //update and delete

    //規範並沒有說明是不是要更新persistent object,所以如果要使用,建議在單獨的trasaction中執行

        Query q =session.createQuery("update Topic t sett.title = upper(t.title)") ;

        q.executeUpdate();

        q = session.createQuery("from Topic");

        for(Object o : q.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getTitle());

        }

        session.createQuery("update Topic t set t.title = lower(t.title)")

            .executeUpdate();

   

//不重要

        Query q =session.getNamedQuery("topic.selectCertainTopic");

        q.setParameter("id", 5);

        Topic t =(Topic)q.uniqueResult();

        System.out.println(t.getTitle());

   

    //Native(瞭解)

        SQLQuery q =session.createSQLQuery("select *from category limit 2,4").addEntity(Category.class);

        List<Category>categories = (List<Category>)q.list();

        for(Category c : categories) {

            System.out.println(c.getName());

        }

   

    public void testHQL_35() {

        //尚未實現JPA命名的NativeSQL

    }

 

第20課 Query by Criteria(QBC)

QBC(Query By Criteria)查詢方式是Hibernate提供的“更加面向對象”的一種檢索方式。QBC在條件查詢上比HQL查詢更爲靈活,而且支持運行時動態生成查詢語句。

 

在Hibernate應用中使用QBC查詢通常經過3個步驟 
  (1)使用Session實例的createCriteria()方法創建Criteria對象 
  (2)使用工具類Restrictions的相關方法爲Criteria對象設置查詢對象 
  (3)使用Criteria對象的list()方法執行查詢,返回查詢結果

一、        實體代碼:

注意:數據是使用Hibernate查詢章節的數據

        //criterion 標準/準則/約束

        Criteria c =session.createCriteria(Topic.class) //from Topic                  

                     .add(Restrictions.gt("id", 2)) //greater than = id > 2

                     .add(Restrictions.lt("id", 8)) //little than = id < 8

                     .add(Restrictions.like("title", "t_"))

                     .createCriteria("category")

                .add(Restrictions.between("id", 3, 5)) //category.id >= 3 and category.id <=5

                     ;

        //DetachedCriterea

        for(Object o : c.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getId()+ "-" + t.getTitle());

        }

二、        Restrictions用法

Hibernate中Restrictions的方法            說明  
Restrictions.eq                         =  
Restrictions.allEq                       利用Map來進行多個等於的限制  
Restrictions.gt                          >  
Restrictions.ge                          >=  
Restrictions.lt                          < 
Restrictions.le                          <=  
Restrictions.between                     BETWEEN 
Restrictions.like                        LIKE 
Restrictions.in                          in 
Restrictions.and                              and 
Restrictions.or                          or 
Restrictions.sqlRestriction              用SQL限定查詢 

============================================

QBE (QueryBy Example)
  Criteria cri = session.createCriteria(Student.class);
  cri.add(Example.create(s)); //s是一個Student對象
  list cri.list();   
  實質:創建一個模版,比如我有一個表serial有一個 giftortoy字段,我設置serial.setgifttoy("2"),
        則這個表中的所有的giftortoy爲2的數據都會出來

2: QBC (Query By Criteria) 主要有Criteria,Criterion,Oder,Restrictions類組成
  session = this.getSession();
  Criteria cri = session.createCriteria(JdItemSerialnumber.class);
  Criterion cron = Restrictions.like("customer",name);
  cri.add(cron);
  list = cri.list();
  ==============================

Hibernate中 Restrictions.or()和Restrictions.disjunction()的區別是什麼?
  比較運算符
  HQL運算符                  QBC運算符                     含義
     =                    Restrictions.eq()                 等於
     <>                  Restrictions.not(Exprission.eq())  不等於
     >                    Restrictions.gt()                 大於
     >=                  Restrictions.ge()                 大於等於
     <                    Restrictions.lt()                 小於
     <=                  Restrictions.le()                 小於等於
     is null            Restrictions.isnull()              等於空值
     is not null      Restrictions.isNotNull()          非空值
     like                Restrictions.like()               字符串模式匹配
     and               Restrictions.and()                邏輯與
     and               Restrictions.conjunction()         邏輯與
     or                  Restrictions.or()                 邏輯或
     or                  Restrictions.disjunction()         邏輯或
     not                 Restrictions.not()                邏輯非
     in(列表)         Restrictions.in()                 等於列表中的某一個值
     ont in(列表)        Restrictions.not(Restrictions.in())不等於列表中任意一個值
     between x and y      Restrictions.between()            閉區間xy中的任意值
     not between x and y Restrictions.not(Restrictions..between()) 小於值X或者大於值y

3: HQL
  String hql = "select s.name ,avg(s.age) from Student s group bys.name";
  Query query = session.createQuery(hql);
  list = query.list();
  ....

4: 本地SQL查詢
  session = sessionFactory.openSession();
  tran = session.beginTransaction();
  SQLQuery sq = session.createSQLQuery(sql);
  sq.addEntity(Student.class);
  list = sq.list();
  tran.commit();

5: QID 
  Session的get()和load()方法提供了根據對象ID來檢索對象的方式。該方式被用於事先知道了要檢索對象ID的情況。

三、 工具類Order提供設置排序方式

Order.asc(String propertyName) 
升序排序 
Order.desc(String propertyName) 
降序排序 

四、 工具類Projections提供對查詢結果進行統計與分組操作

Porjections.avg(String propertyName) 
求某屬性的平均值 
Projections.count(String propertyName) 
統計某屬性的數量 
Projections.countDistinct(String propertyName) 
統計某屬性的不同值的數量 
Projections.groupProperty(String propertyName) 
指定一組屬性值 
Projections.max(String propertyName) 
某屬性的最大值 
Projections.min(String propertyName) 
某屬性的最小值 
Projections.projectionList() 
創建一個新的projectionList對象 
Projections.rowCount() 
查詢結果集中記錄的條數 
Projections.sum(String propertyName) 
返回某屬性值的合計

五、 QBC分頁查詢

  Criteria爲我們提供了兩個有用的方法:setFirstResult(intfirstResult)和setMaxResults(int maxResults).
setFirstResult(int firstResult)方法用於指定從哪一個對象開始檢索(序號從0開始),默認爲第一個對象(序號爲0);setMaxResults(int maxResults)方法用於指定一次最多檢索出的對象數目,默認爲所有對象。

Session session = HibernateSessionFactory.getSessionFactory().openSession();  

Transaction ts = null;  

Criteria criteria = session.createCriteria(Order.class);  

int pageSize = 15;   

int pageNo = 1;   

criteria.setFirstResult((pageNo-1)*pageSize);   

criteria.setMaxResults(pageSize);  

Iterator it = criteria.list().iterator();  

ts.commit();  

HibernateSessionFactory.closeSession();  

六、 QBC複合查詢

   複合查詢就是在原有的查詢基礎上再進行查詢。例如在顧客對定單的一對多關係中,在查詢出所有的顧客對象後,希望在查詢定單中money大於1000的定單對象。

 

Session session = HibernateSessionFactory.getSessionFactory().openSession();  

Transaction ts = session.beginTransaction();  

Criteria cuscriteria = session.createCriteria(Customer.class);  

Criteria ordCriteria = cusCriteria.createCriteria("orders");  

ordCriteria.add(Restrictions.gt("money", new Double(1000)));   

Iterator it = cusCriteria.list().iterator();  

ts.commit();  

HibernateSessionFactory.closeSession();  

 

七、 QBC離線查詢

   離線查詢又叫DetachedCriteria查詢,它可以在Session之外進行構造,只有在需要執行查詢時才與Session綁定。Session session =HibernateSessionFactory.getSessionFactory().openSession();

Transaction ts =session.beginTransaction();

Criteria cuscriteria =session.createCriteria(Customer.class);

Criteria ordCriteria =cusCriteria.createCriteria("orders");

ordCriteria.add(Restrictions.gt("money",new Double(1000)));

Iterator it =cusCriteria.list().iterator();

ts.commit();

HibernateSessionFactory.closeSession();

 

第21課 Query By Example(QBE)

QBE查詢就是檢索與指定樣本對象具有相同屬性值的對象。因此QBE查詢的關鍵就是樣本對象的創建,樣本對象中的所有非空屬性均將作爲查詢條件。QBE查詢的功能子集,雖然QBE沒有QBC功能大,但是有些場合QBE使用起來更爲方便。
        工具類Example爲Criteria對象指定樣本對象作爲查詢條件

一、        實例代碼

Session session = sf.openSession();

        session.beginTransaction();

        Topic tExample = new Topic();

        tExample.setTitle("T_");       

        Example e = Example.create(tExample)

                    .ignoreCase().enableLike();

        Criteria c = session.createCriteria(Topic.class)

                     .add(Restrictions.gt("id", 2))

                     .add(Restrictions.lt("id", 8))

                     .add(e);      

        for(Object o : c.list()) {

            Topic t = (Topic)o;

            System.out.println(t.getId() + "-" + t.getTitle());

        }

        session.getTransaction().commit();

        session.close();

 

 

Session session = HibernateSessionFactory.getSessionFactory().openSession();  

Transaction ts = session.beginTransaction();  

Customer c = new Customer();  

c.setCname("Hibernate");   

Criteria criteria = session.createCriteria(Customer.class);  

Criteria.add(Example.create(c));   

Iterator it = criteria.list().iterator();  

ts.commit();   

HibernateSessionFactory.closeSession();

第22課 Query.list與query.iterate(不太重要)

一、         query.iterate查詢數據

* query.iterate()方式返回迭代查詢

     * 會開始發出一條語句:查詢所有記錄ID語句

     * Hibernate: select student0_.id as col_0_0_from t_student student0_

     * 然後有多少條記錄,會發出多少條查詢語句。

     * n + 1問題:n:有n條記錄,發出n條查詢語句;1 :發出一條查詢所有記錄ID語句。

     * 出現n+1的原因:因爲iterate(迭代查詢)是使用緩存的,

                    第一次查詢數據時發出查詢語句加載數據並加入到緩存,以後再查詢時hibernate會先到ession緩存(一級緩存)中查看數據是否存在,如果存在則直接取出使用,否則發出查詢語句進行查詢。

session= HibernateUtils.getSession();

            tx = session.beginTransaction();

           

            /**

             * 出現N+1問題

             * 發出查詢id列表的sql語句

             * Hibernate: select student0_.id as col_0_0_ from t_student student0_

             *

             * 再依次發出根據id查詢Student對象的sql語句

             * Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_,

             * student0_.createTime as createTime1_0_, student0_.classesid as classesid1_0_

             * from t_student student0_ where student0_.id=?

             */

            Iterator students = session.createQuery("fromStudent").iterate();

           

            while (students.hasNext()){

                Student student =(Student)students.next();

                System.out.println(student.getName());

            }

tx.commit();

 

二、         query.list()和query.iterate()的區別

先執行query.list(),再執行query.iterate,這樣不會出現N+1問題,

     * 因爲list操作已經將Student對象放到了一級緩存中,所以再次使用iterate操作的時候

     * 它首先發出一條查詢id列表的sql,再根據id到緩存中取數據,只有在緩存中找不到相應的

     * 數據時,纔會發出sql到數據庫中查詢

Liststudents = session.createQuery("from Student").list();

           

            for (Iterator iter = students.iterator();iter.hasNext();){

                Student student =(Student)iter.next();

                System.out.println(student.getName());

            }                       System.out.println("---------------------------------------------------------");

             // 不會出現N+1問題,因爲list操作已經將數據加入到一級緩存。

            Iterator iters =session.createQuery("from Student").iterate();

           

            while (iters.hasNext()){

                Student student =(Student)iters.next();

                System.out.println(student.getName());

            }

 

三、         兩次query.list()

     * 會再次發出查詢sql

     * 在默認情況下list每次都會向數據庫發出查詢對象的sql,除非配置了查詢緩存

     * 所以:雖然list操作已經將數據放到一級緩存,但list默認情況下不會利用緩存,而再次發出sql

     * 默認情況下,list會向緩存中放入數據,但不會使用數據。

Liststudents = session.createQuery("from Student").list();

           

            for (Iterator iter = students.iterator();iter.hasNext();){

                Student student =(Student)iter.next();

                System.out.println(student.getName());

            }

           

            System.out.println("------------------------------------------------");

           

            //會再次發現SQL語句進行查詢,因爲默認情況list只向緩存中放入數據,不會使用緩存中數據

            students = session.createQuery("fromStudent").list();

           

            for (Iterator iter = students.iterator();iter.hasNext();){

                Student student =(Student)iter.next();

                System.out.println(student.getName());

            }

 

第23課 性能優化策略

1、  注意session.clear()的動用,尤其在不斷分頁循環的時候

a)        在一個大集合中進行遍歷,遍歷msg,取出其中的含有敏感字樣的對象

b)        另外一種形式的內存泄露 //面試是:Java有內存泄漏嗎?

2、  1 + N問題 //典型的面試題

a)        Lazy

b)        BatchSize   設置在實體類的前面

c)         joinfetch

3、  list 和 iterate不同之處

a)        list取所有

b)        Iterate先取ID,等用到的時候再根據ID來取對象

c)         session中list第二次發出,仍會到數據庫查詢

d)        iterate第二次,首先找session級緩存

 

第24課 hibernate緩存

一、 Session級緩存(一級緩存)

一級緩存很短和session的生命週期一致,因此也叫session級緩存或事務級緩存

         hibernate一級緩存

        

那些方法支持一級緩存:

         * get()

         * load()

         * iterate(查詢實體對象)

        

如何管理一級緩存:

         *session.clear(),session.evict()

        

如何避免一次性大量的實體數據入庫導致內存溢出

         * 先flush,再clear

        

如果數據量特別大,考慮採用jdbc實現,如果jdbc也不能滿足要求可以考慮採用數據本身的特定導入工具

 

二、 二級緩存

Hibernate默認的二級緩存是開啓的。

         二級緩存也稱爲進程級的緩存,也可稱爲SessionFactory級的緩存(因爲SessionFactory可以管理二級緩存),它與session級緩存不一樣,一級緩存只要session關閉緩存就不存在了。而二級緩存則只要進程在二級緩存就可用。

         二級緩存可以被所有的session共享

         二級緩存的生命週期和SessionFactory的生命週期一樣,SessionFactory可以管理二級緩存

         二級緩存同session級緩存一樣,只緩存實體對象,普通屬性的查詢不會緩存

         二級緩存一般使用第三方的產品,如EHCache

 

1、   二級緩存的配置和使用:

         配置二級緩存的配置文件:模板文件位於hibernate\etc目錄下(如ehcache.xml),將模板存放在ClassPath目錄中,一般放在根目錄下(src目錄下)

<ehcache>

    <!-- 設置當緩存對象益出時,對象保存到磁盤時的保存路徑。

            如 d:\xxxx

         The following properties aretranslated:

         user.home - User's home directory

         user.dir - User's current workingdirectory

         java.io.tmpdir - windows的臨時目錄 -->

    <diskStore path="java.io.tmpdir"/>

 

    <!--默認配置/或對某一個類進行管理

        maxInMemory       - 緩存中可以存入的最多個對象數

        eternal           - true:表示永不失效,false:不是永久有效的。

        timeToIdleSeconds - 空閒時間,當第一次訪問後在空閒時間內沒有訪問,則對象失效,單位爲秒

        timeToLiveSeconds - 被緩存的對象有效的生命時間,單位爲秒

      overflowToDisk  當緩存中對象數超過覈定數(益出時)時,對象是否保存到磁盤上。true:保存;false:不保存

                         如果保存,則保存路徑在標籤<diskStore>中屬性path指定

        -->

    <defaultCache

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="120"

        timeToLiveSeconds="120"

        overflowToDisk="true"

        />      

</ehcache>

 

2、   二級緩存的開啓:

         Hibernate中二級緩存默認就是開啓的,也可以顯示的開啓

         二級緩存是hibernate的配置文件設置如下:

<!--開啓二級緩存,hibernate默認的二級緩存就是開啓的 -->

        <property name="hibernate.cache.use_second_level_cache">true</property>

 

3、   指定二級緩存產品提供商:

         修改hibernate的 配置文件,指定二級緩存提供商,如下:

<!--指定二級緩存提供商-->

<property name="hibernate.cache.provider_class">

org.hibernate.cache.EhCacheProvider

</property>

以下爲常見緩存提供商:

Cache

Provider class

Type

Cluster Safe

Query Cache Supported

Hashtable (not intended for production use)

org.hibernate.cache.HashtableCacheProvider

memory

 

yes

EHCache

org.hibernate.cache.EhCacheProvider

memory, disk

 

yes

OSCache

org.hibernate.cache.OSCacheProvider

memory, disk

 

yes

SwarmCache

org.hibernate.cache.SwarmCacheProvider

clustered (ip multicast)

yes (clustered invalidation)

 

JBoss TreeCache

org.hibernate.cache.TreeCacheProvider

clustered (ip multicast), transactional

yes (replication)

yes (clock sync req.)

4、   使用二級緩存

a)       xml方式:指定哪些實體類使用二級緩存:

         方法一:在實體類映射文件中,使用<cache>來指定那個實體類使用二級緩存,如下:

<cache 
    usage="transactional|read-write|nonstrict-read-write|read-only"  (1)
    region="RegionName"                                              (2)
    include="all|non-lazy"                                           (3)
/>

(1)   usage(必須)說明了緩存的策略: transactional、 read-writenonstrict-read-write或 read-only

(2)   region (可選, 默認爲類或者集合的名字(class orcollection role name)) 指定第二級緩存的區域名(name of the secondlevel cache region)

(3)   include (可選,默認爲 allnon-lazy 當屬性級延遲抓取打開時, 標記爲lazy="true"的實體的屬性可能無法被緩存

另外(首選?), 你可以在hibernate.cfg.xml中指定<class-cache>和 <collection-cache> 元素。

這裏的usage 屬性指明瞭緩存併發策略(cache concurrency strategy)

 策略:只讀緩存(Strategy:read only)

如果你的應用程序只需讀取一個持久化類的實例,而無需對其修改, 那麼就可以對其進行只讀 緩存。這是最簡單,也是實用性最好的方法。甚至在集羣中,它也能完美地運作。

<class name="eg.Immutable" mutable="false">
    <cache usage="read-only"/>
    ....
</class>

策略:讀/寫緩存(Strategy:read/write)

如果應用程序需要更新數據,那麼使用/寫緩存 比較合適。 如果應用程序要求“序列化事務”的隔離級別(serializable transaction isolation level),那麼就決不能使用這種緩存策略。 如果在JTA環境中使用緩存,你必須指定hibernate.transaction.manager_lookup_class屬性的值, 通過它,Hibernate才能知道該應用程序中JTA的TransactionManager的具體策略。 在其它環境中,你必須保證在Session.close()、或Session.disconnect()調用前, 整個事務已經結束。 如果你想在集羣環境中使用此策略,你必須保證底層的緩存實現支持鎖定(locking)。Hibernate內置的緩存策略並不支持鎖定功能。

<class name="eg.Cat" .... >
    <cache usage="read-write"/>
    ....
    <set name="kittens" ... >
        <cache usage="read-write"/>
        ....
    </set>
</class>

策略:非嚴格讀/寫緩存(Strategy: nonstrictread/write)

如果應用程序只偶爾需要更新數據(也就是說,兩個事務同時更新同一記錄的情況很不常見),也不需要十分嚴格的事務隔離,那麼比較適合使用非嚴格讀/寫緩存策略。如果在JTA環境中使用該策略,你必須爲其指定hibernate.transaction.manager_lookup_class屬性的值, 在其它環境中,你必須保證在Session.close()、或Session.disconnect()調用前, 整個事務已經結束。

策略:事務緩存(transactional)

Hibernate的事務緩存策略提供了全事務的緩存支持, 例如對JBoss TreeCache的支持。這樣的緩存只能用於JTA環境中,你必須指定爲其hibernate.transaction.manager_lookup_class屬性。

沒有一種緩存提供商能夠支持上列的所有緩存併發策略。下表中列出了各種提供器、及其各自適用的併發策略。

表 19.2.  各種緩存提供商對緩存併發策略的支持情況(Cache Concurrency Strategy Support)

Cache

read-only

nonstrict-read-write

read-write

transactional

Hashtable (not intended for production use)

yes

yes

yes

 

EHCache

yes

yes

yes

 

OSCache

yes

yes

yes

 

SwarmCache

yes

yes

 

 

JBoss TreeCache

yes

 

 

yes

 

注:此方法要求:必須要標籤<cache>放在<id>標籤之前

 

<class name="com.wjt276.hibernate.Student"table="t_student">

        <!--  指定實體類使用二級緩存 -->

        <cache usage="read-only"/>//***********

        <id name="id"column="id">

            <generator class="native"/>

        </id>

        <property name="name"column="name"/>

        <!--

            使用多對一標籤映射 一對多雙向,下列的column值必需與多的一端的key字段值一樣。

         -->

        <many-to-one name="classes"column="classesid"/>

    </class>

 

         方法二:在hibernate配置文件(hibernate.cfg.xml)使用<class-cache>標籤中指定

                   要求:<class-cache>標籤必須放在<maping>標籤之後。

<hibernate-configuration>

    <session-factory>  

        …………

<mapping resource="com/wjt276/hibernate/Classes.hbm.xml"/>

        <mapping resource="com/wjt276/hibernate/Student.hbm.xml"/>

       

        <class-cache class="com.wjt276.hibernate.Student"usage="read-only"/>

    </session-factory>

</hibernate-configuration>

 

    一般推薦使用方法一。

b)       annotation註解

爲了優化數據庫訪問,你可以激活所謂的Hibernate二級緩存.該緩存是可以按每個實體和集合進行配置的.

@org.hibernate.annotations.Cache定義了緩存策略及給定的二級緩存的範圍. 此註解適用於根實體(非子實體),還有集合.

@Entity

@Cache(usage= CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

publicclass Forest { ... }

    @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)

    @JoinColumn(name="CUST_ID")

    @Cache(usage =CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

    public SortedSet<Ticket> getTickets(){

        return tickets;

    }

@Cache(

    CacheConcurrencyStrategy usage();                 (1)

    String region() default "";                       (2)

    String include() default"all";                   (3)

)

(1)

usage: 給定緩存的併發策略(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)

(2)

region (可選的):緩存範圍(默認爲類的全限定類名或是集合的全限定角色名)

(3)

include (可選的):值爲all時包括了所有的屬性(proterty), 爲non-lazy時僅含非延遲屬性(默認值爲all)

 

5、   應用範圍

     沒有變化,近似於靜態的數據。

 

6、   二級緩存的管理:

1、   清除指定實體類的所有數據

SessionFactory.evict(Student.class);

2、   清除指定實體類的指定對象

SessionFactory.evict(Student.class, 1);//第二個參數是指定對象的ID,就可以清除指定ID的對象

使用SessionFactory清除二級緩存

Sessionsession = null;

        try {

            session = HibernateUtils.getSession();

            session.beginTransaction();

           

            Student student = (Student)session.load(Student.class, 1);

            System.out.println("student.name=" +student.getName());

           

            session.getTransaction().commit();

        }catch(Exception e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        }finally {

            HibernateUtils.closeSession(session);

        }

       

        //管理二級緩存

        SessionFactory factory = HibernateUtils.getSessionFactory();

        //factory.evict(Student.class);

        factory.evict(Student.class, 1);

       

        try {

            session = HibernateUtils.getSession();

            session.beginTransaction();

           

            //會發出查詢sql,因爲二級緩存中的數據被清除了

            Student student = (Student)session.load(Student.class, 1);

            System.out.println("student.name=" +student.getName());

           

            session.getTransaction().commit();

        }catch(Exception e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        }finally {

            HibernateUtils.closeSession(session);

    }

 

7、   二級緩存的交互

Sessionsession = null;

        try {

            session = HibernateUtils.getSession();

            session.beginTransaction();

           

            //僅向二級緩存讀數據,而不向二級緩存寫數據

            session.setCacheMode(CacheMode.GET);

            Student student =(Student)session.load(Student.class, 1);

            System.out.println("student.name=" +student.getName());

           

            session.getTransaction().commit();

        }catch(Exception e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        }finally {

            HibernateUtils.closeSession(session);

        }

       

        try {

            session = HibernateUtils.getSession();

            session.beginTransaction();

           

            //發出sql語句,因爲session設置了CacheMode爲GET,所以二級緩存中沒有數據

            Student student =(Student)session.load(Student.class, 1);

            System.out.println("student.name=" +student.getName());

           

            session.getTransaction().commit();

        }catch(Exception e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        }finally {

            HibernateUtils.closeSession(session);

        }

       

        try {

            session = HibernateUtils.getSession();

            session.beginTransaction();

           

            //只向二級緩存寫數據,而不從二級緩存讀數據

            session.setCacheMode(CacheMode.PUT);

           

            //會發出查詢sql,因爲session將CacheMode設置成了PUT

            Student student =(Student)session.load(Student.class, 1);

            System.out.println("student.name=" +student.getName());

           

            session.getTransaction().commit();

        }catch(Exception e) {

            e.printStackTrace();

            session.getTransaction().rollback();

        }finally {

            HibernateUtils.closeSession(session);

        }

CacheMode參數用於控制具體的Session如何與二級緩存進行交互。

·                                CacheMode.NORMAL - 從二級緩存中讀、寫數據。

·                                CacheMode.GET - 從二級緩存中讀取數據,僅在數據更新時對二級緩存寫數據。

·                                CacheMode.PUT - 僅向二級緩存寫數據,但不從二級緩存中讀數據。

·                                CacheMode.REFRESH - 僅向二級緩存寫數據,但不從二級緩存中讀數據。通過hibernate.cache.use_minimal_puts的設置,強制二級緩存從數據庫中讀取數據,刷新緩存內容。

如若需要查看二級緩存或查詢緩存區域的內容,你可以使用統計(Statistics API。

Map cacheEntries = sessionFactory.getStatistics()
        .getSecondLevelCacheStatistics(regionName)
        .getEntries();

此時,你必須手工打開統計選項。可選的,你可以讓Hibernate更人工可讀的方式維護緩存內容。

hibernate.generate_statistics true
hibernate.cache.use_structured_entries true

8、   總結

load默認使用二級緩存,iterate默認使用二級緩存

list默認向二級緩存中加數據,但是查詢時候不使用

三、 查詢緩存

查詢緩存,是用於緩存普通屬性查詢的,當查詢實體時緩存實體ID。
默認情況下關閉,需要打開。查詢緩存,對list/iterator這樣的操作會起作用。
可以使用<property name=”hibernate.cache.use_query_cache”>true</property>來打開查詢緩存,默認爲關閉。
所謂查詢緩存:即讓hibernate緩存list、iterator、createQuery等方法的查詢結果集。如果沒有打開查詢緩存,hibernate將只緩存load方法獲得的單個持久化對象。
在打開了查詢緩存之後,需要注意,調用query.list()操作之前,必須顯式調用query.setCachable(true)來標識某個查詢使用緩存。
查詢緩存的生命週期:當前關聯的表發生修改,那麼查詢緩存生命週期結束
注意查詢緩存依賴於二級緩存,因爲使用查詢緩存需要打開二級緩存

查詢緩存的配置和使用:

    * 在hibernate.cfg.xml文件中啓用查詢緩存,如:

    <propertyname="hibernate.cache.use_query_cache">true</property>

    * 在程序中必須手動啓用查詢緩存,如:

         query.setCacheable(true);
 
例如:

session= HibernateUtils.getSession();

            session.beginTransaction();

            Query query = session.createQuery("selects.name from Student s");

            //啓用查詢查詢緩存

            query.setCacheable(true);

           

            List names = query.list();

            for (Iterator iter=names.iterator();iter.hasNext();) {

                String name =(String)iter.next();

                System.out.println(name);

            }

            System.out.println("-------------------------------------");

            query = session.createQuery("selects.name from Student s");

            //啓用查詢查詢緩存

            query.setCacheable(true);

           

            //沒有發出查詢sql,因爲啓用了查詢緩存

            names = query.list();

            for (Iteratoriter=names.iterator();iter.hasNext(); ) {

                String name =(String)iter.next();

                System.out.println(name);

            }

         session.getTransaction().commit();
 

Session session = sf.openSession();

        session.beginTransaction();

        List<Category>categories = (List<Category>)session.createQuery("from Category")

                                    .setCacheable(true).list();

        session.getTransaction().commit();

        session.close();

       

        Session session2 = sf.openSession();

        session2.beginTransaction();

        List<Category>categories2 = (List<Category>)session2.createQuery("from Category")

        .setCacheable(true).list();

       

        session2.getTransaction().commit();

         session2.close();
 
注:查詢緩存的生命週期與session無關。
查詢緩存只對query.list()起作用,query.iterate不起作用,也就是query.iterate不使用

四、 緩存算法

1、  LRU、LFU、FIFO

 

第25課 事務併發處理

一、 數據庫的隔離級別:併發性作用。

1、   ReadUncommited(未提交讀):沒有提交就可以讀取到數據(發出了Insert,但沒有commit就可以讀取到。)很少用

2、   ReadCommited(提交讀):只有提交後纔可以讀,常用,

3、   RepeatableRead(可重複讀):mysql默認級別, 必需提交才能見到,讀取數據時數據被鎖住。

4、   Serialiazble(序列化讀):最高隔離級別,串型的,你操作完了,我纔可以操作,併發性特別不好,

隔離級別

是否存在髒讀

是否存在不可重複讀

是否存在幻讀

Read Uncommitted(未提交讀)

Y

Y

Y

Read Commited(提交讀)

N

Y(可採用悲觀鎖解決)

Y

Repeatable Read(可重複讀)

N

N

Y

Serialiazble(序列化讀)

 

 

 








髒讀:沒有提交就可以讀取到數據稱爲髒讀

不可重複讀:再重複讀一次,數據與你上的不一樣。稱不可重複讀。

幻讀:在查詢某一條件的數據,開始查詢的後,別人又加入或刪除些數據,再讀取時與原來的數據不一樣了。

1、   Mysql查看數據庫隔離級別:

方法:select@@tx_isolation;


2、   Mysql數據庫修改隔離級別:

方法:set transactionisolation level 隔離級別名稱;


例如:修改爲未提交讀:settransaction isolation level read uncommitted;


二、 事務概念(ACID)

ACID即:事務的原子性、一致性、獨立性及持久性 
事務的原子性:是指一個事務要麼全部執行,要麼不執行.也就是說一個事務不可能只執行了一半就停止了.比如你從取款機取錢,這個事務可以分成兩個步驟:1劃卡,2出錢.不可能劃了卡,而錢卻沒出來.這兩步必須同時完成.要麼就不完成.
事務的一致性:是指事務的運行並不改變數據庫中數據的一致性.例如,完整性約束了a+b=10,一個事務改變了a,那麼b也應該隨之改變.
事務的獨立性:是指兩個以上的事務不會出現交錯執行的狀態.因爲這樣可能會導致數據不一致. 
事務的持久性:是指事務運行成功以後,就係統的更新是永久的.不會無緣無故的回滾.

三、 事務併發時可能出現問題

1、第一類丟失更新(Lost Update)

時間

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

第26課 hibernate悲觀鎖、樂觀鎖

Hibernate談到悲觀鎖、樂觀鎖,就要談到數據庫的併發問題,數據庫的隔離級別越高它的併發性就越差

         併發性:當前系統進行了序列化後,當前讀取數據後,別人查詢不了,看不了。稱爲併發性不好

    數據庫隔離級別:見前面章級

一、 悲觀鎖

悲觀鎖:具有排他性(我鎖住當前數據後,別人看到不此數據)

悲觀鎖一般由數據機制來做到的。

1、   悲觀鎖的實現

通常依賴於數據庫機制,在整修過程中將數據鎖定,其它任何用戶都不能讀取或修改(如:必需我修改完之後,別人纔可以修改)

2、   悲觀鎖的適用場景:

悲觀鎖一般適合短事務比較多(如某一數據取出後加1,立即釋放)

長事務佔有時間(如果佔有1個小時,那麼這個1小時別人就不可以使用這些數據),不常用。

3、   實例:


yon

用戶1、用戶2 同時讀取到數據,但是用戶2先 -200,這時數據庫裏的是800,現在用戶1也開始-200,可是用戶1剛纔讀取到的數據是1000,現在用戶用剛剛一開始讀取的數據1000-200爲800,而用戶1在更新時數據庫裏的是用房更新的數據800,按理說用戶1應該是800-200=600,而現在是800,這樣就造成的更新丟失。這種情況該如何處理呢,可採用兩種方法:悲觀鎖、樂觀鎖。先看看悲觀鎖:用戶1讀取數據後,用鎖將其讀取的數據鎖上,這時用戶2是讀取不到數據的,只有用戶1釋放鎖後用戶2纔可以讀取,同樣用戶2讀取數據也鎖上。這樣就可以解決更新丟失的問題了。

實體類:

public class Inventory {

    private int itemNo;

    privateString itemName;   

    private int quantity;

    public intgetItemNo() {

        return itemNo;

    }

    public voidsetItemNo(intitemNo) {

        this.itemNo =itemNo;

    }

    publicString getItemName() {

        return itemName;

    }

    public voidsetItemName(String itemName) {

        this.itemName =itemName;

    }

    public intgetQuantity() {

        return quantity;

    }

    public voidsetQuantity(intquantity) {

        this.quantity =quantity;

    }  

}

映射文件:

<hibernate-mapping>

    <class name="com.wjt276.hibernate.Inventory"table="t_inventory">

        <id name="itemNo">

            <generator class="native"/>

        </id>

        <property name="itemName"/>

        <property name="quantity"/>

    </class>

</hibernate-mapping>

4、   悲觀鎖的使用

如果需要使用悲觀鎖,肯定在加載數據時就要鎖住,通常採用數據庫的for update語句。

Hibernate使用Load進行悲觀鎖加載。

Session.load(Classarg0, Serializable arg1, LockMode arg2) throws HibernateException

LockMode:悲觀鎖模式(一般使用LockMode.UPGRADE)

session= HibernateUtils.getSession();

            tx = session.beginTransaction();

            Inventory inv =(Inventory)session.load(Inventory.class, 1,LockMode.UPGRADE);

            System.out.println(inv.getItemName());

            inv.setQuantity(inv.getQuantity()-200);

           

            session.update(inv);

            tx.commit();

5、   執行輸出SQL語句:

Hibernate:select inventory0_.itemNo as itemNo0_0_, inventory0_.itemName as itemName0_0_,inventory0_.quantity as quantity0_0_ from t_inventory inventory0_ where inventory0_.itemNo=?for update //在select語句中加入for update進行使用悲觀鎖。

腦白金

Hibernate:update t_inventory set itemName=?, quantity=? where itemNo=?

注:只有用戶釋放鎖後,別的用戶纔可以讀取

 

注:如果使用悲觀鎖,那麼lazy(悚加載無效)

 

二、 樂觀鎖

樂觀鎖:不是鎖,是一種衝突檢測機制。

     樂觀鎖的併發性較好,因爲我改的時候,別人隨邊修改。

     樂觀鎖的實現方式:常用的是版本的方式(每個數據表中有一個版本字段version,某一個用戶更新數據後,版本號+1,另一個用戶修改後再+1,當用戶更新發現數據庫當前版本號與讀取數據時版本號不一致(等於小於數據庫當前版本號),則更新不了。

         Hibernate使用樂觀鎖需要在映射文件中配置項纔可生效。

實體類:

public classInventory {

    private int itemNo;

    privateString itemName;   

    private int quantity;  

    private int version;//Hibernate用戶實現版本方式樂觀鎖,但需要在映射文件中配置

    public intgetItemNo() {

        return itemNo;

    }

    public voidsetItemNo(intitemNo) {

        this.itemNo =itemNo;

    }

    publicString getItemName() {

        return itemName;

    }

    public voidsetItemName(String itemName) {

        this.itemName =itemName;

    }

    public intgetQuantity() {

        return quantity;

    }

    public voidsetQuantity(intquantity) {

        this.quantity =quantity;

    }

    public intgetVersion() {

        return version;

    }

    public voidsetVersion(intversion) {

        this.version =version;

    }

}

映射文件

<hibernate-mapping>

<!-- 映射實體類時,需要加入一個開啓樂觀鎖的屬性

optimistic-lock="version" 共有好幾種方式:

    - none  -version   - dirty - all

    同時需要在主鍵映射後面映射版本號字段

    -->

<class name="com.wjt276.hibernate.Inventory"table="t_inventory"optimistic-lock="version">

        <id name="itemNo">

            <generator class="native"/>

        </id>

        <version name="version"/><!—必需配置在主鍵映射後面 -->

        <property name="itemName"/>

        <property name="quantity"/>

    </class>

</hibernate-mapping>

導出輸出SQL語句:

createtable t_inventory (itemNo integer not null auto_increment, versioninteger not null, itemName varchar(255), quantity integer,primary key (itemNo))

注:添加的版本字段version,還是我們來維護的,是由hibernate來維護的。

樂觀鎖在存儲數據時不用關心

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章