hibernate學習筆記(總結)

 

hibernate學習筆記

課程內容 6

1 HelloWorld 6

2 Hibernate原理模擬 - 什麼是O/R Mapping以及爲什麼要有O/R Mapping 6

3 常見的0/R框架(瞭解) 6

4 hibernate基礎配置(重點) 6

5 ID生成策略(重點 AUTO) 6

6 Hibernate核心開發接口介紹(重點) 6

7 對象的三種狀態(瞭解) 6

8 關係映射(重點) 6

9 Hibernate査詢(HQL) 6

10 Struts基礎上繼續完善BBS200 6

11 性能優化(重點) 6

12 補充話題 6

風格 6

1 先脈絡,後細節 6

2 先操作,後原理 6

3 Annotation,xml配置文件 6

資源 6

1 http://www. hibernate.org 6

2 hibernate zh_CN文檔 6

3 hibernate annotation references 6

環境準備 6

1 下載hibernate-distribution-3.3.2.GA-dist 6

2 下載hibernate-annotations-3[1].4.0.GA 6

3 注意閱讀hibernate compatibility matrix(hibernate 網站download) 6

4 下載slf4jl.5.8 7

Hibernate HelloWorld 7

1 建立新java 項目,名爲hibernate_0100_HelloWorld 7

2 學習建User-library-hibernate,並加入相應的jar 7

3 引入mysqlJDBC驅動包 7

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

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

6 建立Student  7

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

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

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

10 FAQ 7

11 Note: 7

12 建立能力 8

建立 Annotation 版本的 HelloWorld 8

1 創建teacher 表,create table teacher (id int primary key, name varhcar(20), title varchar(lO)); 8

2 創建Teacher  8

3 hibernate lib 中加入annotationjar 8

4 參考Annotaion文檔建立對應的註解 8

5 hibernate.cfg.xml中建立映射<mapping class:.../ 8

6 參考文襠進行測試(注意文襠中缺少configure()的小bug) 8

7 FAQ: @不給提示 8

What is and Why 0/R Mapping 8

1 JDBC操作數據庫很繁瑣 8

2 Sql語句編寫並不是面向對象的 8

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

4 0/R Mapping 簡化編程 8

5 0/R Mapping跨越數據庫平臺 8

6 Hibernate_0200_OR_Mapping_Simulation 8

0/R Mapping Frameworks 8

1 hibernate 8

2 toplink 9

3 jdo 9

4 JPA 9

Hibernate基礎配置 9

1 對應項目:Hibernate_0300_BasicConfiguration 9

2 介紹MSQL的圖形化客戶端 9

3 hibernate.cfg.xml: hbni2ddl.auto 9

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

5 搭建jUnit環境 9

6 hibernate.cfg.xmlshow_sql 9

7 hibernate.cfg.xmlformat_sql 9

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

9 字段名和屬性相同 9

10 字段名和屬性名不同 9

11 不需要psersistence的字段(不用列) 9

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

13 映射枚舉類型比較少用) 10

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

15 @Lob 10

16 課外:CLOBBLOB類型的數據存取 10

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

18 hibernate 類型 10

ID生成策略 10

1 對應項目:hibernate_0400_ID 10

2 注意: 10

3 xml生成id 10

4 註解方式:@GeneratedValue 10

5 FAQ; 11

6 聯合主鍵 11

核心幵發接口介紹 13

1 hibernate_0500_CoreAPI 13

2 Hibernate API文檔需要單獨下載 13

3 Configuration 13

4 SessoinFactor 13

5 Session 13

6 SchemaExport (自動建表) 14

7 Query 接口 14

8 Note 14

三種對象狀態 15

1 上一個 project 15

2 三種狀態的區分關鍵在於 15

3 三種狀態: 15

4 對這三種狀態需要關注的問題是在該狀態下如果進行數據庫的操作會發生什麼結果,比 如改變屬性的 15

關係映射(重要) 16

對象之間的關係 16

1 這裏的關係映射指的是對象之間的關係,並不是指數據庫的關係,本章解決的問題是當對象之間處於 16

2 簡化問題: 16

3 對一 16

4 對多 16

5 組件映射 16

一對一關聯 17

1 一對一單向外鍵關聯 17

2 一對一雙向外鍵關聯 17

3 一對一單向主鍵關聯(不重要) 18

4 一對一雙向主鍵關聯(不重要) 18

5 聯合主鍵 18

組件映射 18

1 項目:hibernate_1100_component 18

2 對象關係:一個對象是另外一個對象的一部分 18

3 數據庫表:一張表 18

4 annotation: @ Embeddable @Embbeded 18

5 xml: 使用<component,例如: 19

多對一與一對多 19

1 多對一單向關聯 19

2 一對多單向關聯 20

3 一對多(多對一)雙向關聯 20

多對多 21

1 單向關聯: 21

2 雙向關聯: 22

關聯關係中的CRUD_Cascade_Fetch 22

1 hibernate_1700_one2many_many2one_bi_crud 22

2 設定cascade以設定在持久化時對於關聯對象的操作(CUDRFetch管) 22

3 cascade僅僅是幫我們省了編程的麻煩而已,不要把它的作用看的太大 22

4 鐵律:雙向關係在程序中要設定雙向關聯 23

5 鐵律:雙向mappedBy 23

6 fetch 23

7 Update@ManyToOne()中的cascade參數關係 23

8 Delete@ManyToOne()中的cascade關係 23

9 O/RMapping 編程模型 24

10 要想刪除或者更新先做load,除了精確知道ID之外 24

11 如果想消除關聯關係,先設定關係爲null.再刪除對應記錄,如果不刪記錄,該記錄變成垃圾數據 24

12 練習:多對多的CRUD 24

關係映射總結 25

1 什麼樣的關係,設計什麼樣的表,進行什麼樣的映射 25

2 CRUD,按照自然的理解即可(動手測試) 25

集合映射(不太重要) 25

1 項目名稱:hibernate_1800_Collections_Mapping 25

2 Set 25

3 List (Set差不多 多個@OrderBy) 25

4 Map 25

繼承映射(不太重要) 25

1 三種方式 25

作業: 25

1 學生課程、分數的設計(重要) 25

2 設計: 26

3 樹狀結構的設計(至關重要) 26

Hibernate 查詢(Query Language) 27

HQL vs EJBQL 27

1 NativeSQL >HQL.> EJBQL(JPQL 1.0) > QBC(Query By Criteria) > QBE(Query By Example)" 27

2 總結:QL應該和導航關係結合,共同爲査詢提供服務。 27

性能優化 27

1 注意session.clear()的運用,尤其在不斷分頁循環的時候 27

2 1+N問題 (典型的面試題) (詳見hibernate_2800_Hibernate_1+N項目) 27

3 listiterate不同之處(//主要爲了面試  詳見hibernate_2900_Hibernate_list_iterate 27

4 一級緩存和二級緩存和査詢緩存(面試題)(詳見hibernate_3000_Hibernate_3KindsOf_Cache) 27

5 事務併發處理(面試的意義更大) 28

課程內容

HelloWorld

a) Xml

b) annotation

Hibernate原理模擬 - 什麼是O/R Mapping以及爲什麼要有O/R Mapping

常見的0/R框架(瞭解)

hibernate基礎配置(重點)

ID生成策略(重點 AUTO) 

Hibernate核心開發接口介紹(重點)

對象的三種狀態(瞭解)

關係映射(重點)

Hibernate査詢(HQL

10 Struts基礎上繼續完善BBS200

11 性能優化(重點)

12 補充話題

風格

先脈絡,後細節

先操作,後原理

Annotation,xml置文件

a) JPA

b) hibernate – extension

資源

http://www. hibernate.org

hibernate zh_CN文檔

hibernate annotation references

環境準備

下載hibernate-distribution-3.3.2.GA-dist

下載hibernate-annotations-3[1].4.0.GA

注意閱讀hibernate compatibility matrix(hibernate 網站download)

下載slf4jl.5.8

Hibernate HelloWorld

建立新java 項目,名爲hibernate_0100_HelloWorld

學習建User-library-hibernate,並加入相應的jar

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

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

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

i. hibernate core

ii. /required

iii. slf-nop jar

引入mysqlJDBC驅動包

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

a) create database hibernate

b) use hibernate

c) create table Student (id int primary key, namevarchar(20), age int)

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

a) 從參考文檔中copy

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

c) 註釋掉暫時用不上的內容

建立Student 

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

a) 參考文檔

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

a) 參考文檔

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

a) 參考文擋

10 FAQ

a) 要調用 new Configuration().configure().buildSessionFactory(),而不是

省略 configure,否則會出 hibernate dialect must be set 的異常

11 Note:

a) 請務必建立自己動手査文擋的能力

b) 重要的是:

i. 要建立自己動手查一手文檔的信心

ii. 還有建立自己動手查一手文檔的習慣!

iii. 主動學習,砍棄被動接受灌輸的習慣!

12 建立能力

a) 錯誤讀完整

b) 昔誤的關鍵行

c) 排除法

d) 比較法

e) google

建立 Annotation 版本的 HelloWorld

創建teacher 表,create table teacher (id int primary key, name varhcar(20), title varchar(lO));

創建Teacher 

hibernate lib 中加入annotationjar

a) hibernate annotaion jar

b) ejb3 persistence jar

c) hibernate common-annotations.jar

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

參考Annotaion文檔建立對應的註解

hibernate.cfg.xml中建立映射<mapping class:.../

參考文襠進行測試(注意文襠中缺少configure()的小bug)

FAQ: @不給提示

a) 配置eclipse屬性信息content assist-activation--加上@

What is and Why 0/R Mapping

JDBC操作數據庫很繁瑣

Sql語句編寫並不是面向對象的

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

0/R Mapping 簡化編程

0/R Mapping跨越數據庫平臺

Hibernate_0200_OR_Mapping_Simulation

0/R Mapping Frameworks

hibernate

toplink

jdo

JPA

a) 意願統一天下

Hibernate基礎配置

對應項目:Hibernate_0300_BasicConfiguration

介紹MSQL的圖形化客戶端

hibernate.cfg.xml: hbni2ddl.autocreateupdate。。。。

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

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

a) slf4jlog4j的關係:slf4j像是一個大管家,可以管理許多的日誌框架,log4j是其中之一

b) 加入slf4j-log4j.jar,加入 log4j  jar ,去掉 slf4-nop.jar

c) hibernate/project/etc 目錄 copy log4j.properties

d) 査詢hibernate文襠,日誌部分,調整日誌的輸出策略

搭建jUnit環境

a) 需要注意jUnitBug

hibernate.cfg.xmlshow_sql  是否輸出SQL語句

hibernate.cfg.xmlformat_sql 格式化SQL語句,美化SQL語句

   <!-- 格式化顯示輸出sql -->

   <property name="format_sql">true</property>

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

a) Annotation: @Table

b) xml:自己査詢

字段名和屬性相同

a) 不用寫@column 默認@Basic效果一樣

b) Xml中不用寫 column

10 字段名和屬性名不同

a) Annotation: @Column

b) xml:自己査詢

11 不需要psersistence的字段(不用列)

a) Annotation@Transient 定義不寫入數據庫,屬性透明

b) xml不寫

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

a) Annotation@Temporal(參數參數有3 只顯示時間,只顯示日期,時間日期都顯示

//@Temporal(TemporalType.DATE)  只顯示日期

//@Temporal(TemporalType.TIME)  只顯示時間

//@Temporal(TemporalType.TIMESTAMP)  顯示日期與時間

b) xml:指定 type

<class name="Teacher" table="Teacher" >

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

<property name="name" type="time" />

</class>

13 映射枚舉類型比較少用)

a) @Enumerated

@Enumerated(EnumType.ORDINAL) 枚舉類型按位置數,:0,1,2 ...存儲

@Enumerated(EnumType.STRING)  枚舉類型按設定值存儲

b) xml:麻煩

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

a) best practice保持 field(變量定義)  get set 方法的一致

15 @Lob

16 課外:CLOBBLOB類型的數據存取

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

18 hibernate 類型

ID生成策略

對應項目:hibernate_0400_ID

注意:

a)  我們觀察hibernate生成表的結構並不是爲了將來就用它生成,(可能還有自己的擴展,比如index等)而是爲了明白我們應該建立什麼樣的表和實體類映射

xml生成id

a) generator

<id name="id" >

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

</id>

b) 常用四個:native identity sequence uuid

註解方式:@GeneratedValue

a) 自定義ID

b) AUTO(直接寫 @GeneratedValue 相當如native) (@GeneratedValue(strategy=GenerationType.AUTO))

i. 默認:對 MySQL,使用auto_increment

ii.  Oracle使用hibernate_sequence(名稱固定)

c) IDENTITY(@GeneratedValue(strategy=GenerationType.IDENTITY))

d) SEQUENCE(@GeneratedValue(strategy=GenerationType.SEQUENCE))

i. @SequenceGenerator(可自定義在數據庫生成指定的sequence名)

   @Id

//@GeneratedValue中增加 generator="teacherSEQ"

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

//"teacherSEQ"@SequenceGenerator的標識名

//"teacherSEQ_DB"爲指定到數據庫生成的Sequence

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

public int getId() {

return id;

}

e) TABLE (可以忘記)

i. @TableGenerator

@TableGenerator(

name="teacherID", //被調用的TABLE名字

table="teacherID_DB", //數據庫建立的表名

pkColumnName="key_value",

pkColumnValue="pk_value",

valueColumnName="teacher", //pkColumnValue對應類名

allocationSize=1 //pkColumnValue對應類名

)

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

注:如果使用註解方式的uuid 如下:

@Id

@GeneratedValue(generator="teacherUUID")

@GenericGenerator(name="teacherUUID", strategy="uuid")

FAQ;

a) Junit測試時Hibernate Session Factory初始化異常不提示.疑似一個bug

b) main來做測試

聯合主鍵

a) Xml方式: composite-id

i. 將聯合主鍵的屬性提取出來,重新編寫一個pojo類(原pojo類中的id,name要刪除 並新加入屬性“StudentPK”)

      public class StudentPK implements Serializable {

private String id;

private String name;

 

ii. 新建pojo類必須實現 java.io.Serializable 序列化接

iii. 新pojo類要重寫equalshashCode方法

@Override

public boolean equals(Object o) {

if(o instanceof StudentPk) {

StudentPk pk = (StudentPk)o;

if(this.id == pk.getId() && this.name.equals(pk.getName())) {

  return true;

}

}

return false;

}

@Override

public int hashCode() {

return this.name.hashCode();

}

iv. 聯合主鍵生成策略XML配置方法

    <hibernate-mapping>

<class name="com.bjsxt.pojo.Student" >

<composite-id name="studentPK" class="com.bjsxt.pojo.StudentPK">

<key-property name="id"></key-property>

<key-property name="name"></key-property>

</composite-id>

<property name="age" />

<property name="sex" />

<property name="good" type="yes_no"></property>

</class>

</hibernate-mapping>

b) Annotation

i. 前三步與Xml方式前三步一樣 都要建立新pojo類 都要實現Serializable接口 重寫equalshashCode方法.

ii. 方法1在新類前寫@Embeddable,在原pojo類的新屬性“TercherPK”的get方法前寫@ld,如下

     @ Embeddable

public class TeacherPK implements Serializable {

private String id;

private String name;

 

   @Entity

public class Teacher {

private TeacherPK teacherPK ;

@Id

public TeacherPK getTeacherPK() {

return teacherPK;

}

  

iii. 方法2:@EmbeddedlD(*)  新pojo類無需加註解,只需在原pojo新屬性“TercherPK”的get方法前寫@EmbeddedlD即可

iv. 方法3@Id  @IdClass(*)  pojo類無需加註解,原pojo類的idname屬性保留不變,也無需新增“TercherPK”屬性。 只在idnameget方法前都加@Id,並在原pojo類前加@IdClass(TeacherPK).class),如下

@Entity

@IdClass(TeacherPK.class)

public class Teacher {

private String id;

private String name;

@Id

public String getId() {

return id;

}

@Id

public String getName() {

return name;

}

... ...

核心幵發接口介紹

hibernate_0500_CoreAPI

Hibernate API文檔需要單獨下載

Configuration

a) AnnotationConfiguration

b) 進行配置信息的管理

c) 用來產生SessionFactory

d) 可以在configure方法中指定hibernate配置文件

e) 只氣關注一個方法即:buildSessionFactory

SessoinFactor

a) 用來產生和管理Session

b) 通常情況下每個應用只需要一個SessionFactory

c) 除非要訪間多個數據庫的情況

d) 關注兩個方法即:openSession getCurrentsession

i. open session每次都是新的,需要close

ii. getCurrentsession從上下文找,如果有,用舊的,如果沒有,建新的

1. 用途,界定事務邊界

2. 事務提交自動close

3. 上下文配置可參見xml文件中

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

4. current_session_context_class (jtathread常用 managedcustom.Class少用

a) thread 使用connection 但數據庫連接管理事務

b)jta (全稱java transaction api)-java分佈式事務管理多數據庫訪問

jta由中間件提供(jboss WebLogic,tomcat不支持

Session

a) 管理一個數據庫的任務單元(簡單說就是增 刪 改 查

b) 方法(CRUD)

i. Save() session.save(對象);

ii. Delete session.delete(對象);

iii. Load Student s1=(Student)session.load(Student.class, 1);

iv. Get Student s1=(Student)session.get(Student.class, 1);

v. getload的區別(面試重點,原理

1. 不存在對應記錄時表現不一樣

2. load返回的是代理對象等到真正用到對象的內容時才發出sql語句

3. get直接從數據庫加載不會延遲

vi. updates session.update(對象);

1. 用來更新detached對象更新完成後轉爲persistent狀態

2. 更新transient對象會報錯

3. 更新自己設定idtransient對象可以(數據庫有對應記錄)

4. persistent狀態的對象只要設定(如:t.setName)不同字段就會發生更新

5. 更新部分更改的字段

a) xml 設定 property 標籤的 update 屬性,annotation 設定@Column  updatable

屬性,不過這種方式很少用,因爲不靈活(忘記

b) 使用xml中的dynamic-updateJPA1.0 Annotation 沒有對應的屬性,hibernate 

展? 

i. 同一個session可以,跨session不行,不過可以用merge()(不重要

c) 使用 HQL(EjBQL)(建議

vii. saveOrUpdate() session.saveOrUpdate(對象);

viii. clear方法 session.clear();

1.無論是load還是get,都會首先査找緩存(一級緩存),如果沒有,纔會去數據庫査找,調用

clear()方法可以強制清除session緩存

ix. flush()方法 session.flush();

1. 當session的事務提交後,會強制內存(session緩存)數據庫同步.默認情況下是session的事務提交(commit)時才同步!

2. session的FlushMode設置,可以設定在什麼時候同步緩存與數據庫(很少用)

例如session.setFlushMode(FlushMode.AUTO)

x. find方法已經過時!

SchemaExport (自動建表)

new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);

Query 接口

a) 參考Hibernate査詢(HQLEJBQL)的內容

Note

a) Hibernate中涉及很多非常非常細節的區別,但在實際應用中用得極少,請大家先享受寫項目的樂

趣,再來探討這些細節問題

i. 比如savepersist的區別

ii. mergeevict 等方法

iii. 比如 refreshlock 

b) 建議的學習方法,動手實驗

c) 細節問題參考補充視頻

三種對象狀態

上一個 project

三種狀態的區分關鍵在於

a) 有沒有ID

b) ID在數據庫中有沒有

c) 在內存中有沒有(session緩存)

三種狀態:

a) transient:內存中一個對象,沒ID,緩存中也沒有

b) persistent:內存中有,緩存中有,數據庫有(ID) 

c) detached:內存有,緩存沒有,數據庫有,ID

對這三種狀態需要關注的問題是在該狀態下如果進行數據庫的操作會發生什麼結果,比 如改變屬性的

值會不會發出update語句?

a) 強烈建議動手實驗

b) 進行正常人的思考

c) 絕對不要背這些東西!背過也並不代表你有多牛!

關係映射(重要)

對象之間的關係

這裏的關係映射指的是對象之間的關係,並不是指數據庫的關係,本章解決的問題是當對象之間處於

下列關係之一時,數據庫表該如何映射,編程上該如何對待(紅色爲重點中的重點)

簡化問題:

a) 怎麼寫 Annotation

b) 增刪改査CRUD怎麼寫

對一

a) 單向(主鍵、外鍵)

b) 雙向(主鍵、外鍵)

c) 中間表

對多

a) 一張主表,多張子表

組件映射

a) @Embeddable

b) @ Embedded

一對一關聯

一對一單向外鍵關聯

a) 項目名稱:hibernate_0600_one2one_uni_fk

b) Annotation: 在被約束表字段的get方法上加@0ne20ne @JoinColumn

@OneToOne

@JoinColumn(name="wifeid") //指定生成的數據庫字段名

public Wife getWife() {

return wife;

}

c) xml: 在被約束表的xml配置文件中加<many-to-one unique

<class name="com.bjsxt.pojo.StuIdCard">

<id name="id">

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

</id>

<property name="num"/>

<many-to-one name="student" column="studentId" unique="true">

</many-to-one>

</class>

unique="true"是保證生成的字段唯一,這樣<many-to-one 也達到了一對一的效果

 一對一雙向外鍵關聯

a) 項目名稱:hibernate_0700_one2one_bi_fk^

b) Annotation: @0ne20ne(mappedBy=另一個類裏定義的屬性名)

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

在Wife類中 寫Husband對象屬性 並添加註解@OneToOne(mappedBy="wife"mappedBy作用

是指定這個一對一關聯是被Husband類的 wife屬性(準確說是getWife方法)做的映射

@OneToOne(mappedBy="wife")

    public Husband getHusband() {

return husband;

}

在 類中寫Wife對象屬性

@OneToOne

@JoinColumn(name="wifeid") //指定生成的數據庫字段名

public Wife getWife() {

return wife;

}

此註釋將由Husband表中生成wifeid字段作爲fk外鍵,wife表中不生成額外的Husbandid字段

c) xml: many-to-one unique <one-to-one property-ref

在Student類中寫StuIdCard屬性, StuIdCard類中寫Student屬性

StuIdCard.hbm.xml文件中加

<many-to-one name="student" column="studentId" unique="true"></many-to-one>

Student.hbm.xml文件中加

<one-to-one name="stuIdCard" property-ref="student"></one-to-one>

其中, property-ref 相當於mappedBy

此方式生成的StuIdCard表中包含studentid字段作爲fk外鍵, Student表中不生成額外的字段

特別說明: 一對一單向外鍵關聯與一對一雙向外鍵關聯在數據庫的表的格式是一樣的,區別在於

java程序中. 雙向外鍵關聯可通過Hibernate在兩個類間互相調用彼此,而單向外鍵關聯只能單方向調用.

一對一單向主鍵關聯(不重要,忘記

a) 項目名稱:hibernate_0800_one2one_uni_pk

b) @primaryKeyJoinColumn

c) xml: <one-to-one id 使用 foreign class

對一雙向主鍵關聯(不重要,忘記

a) 項目名稱:hibernate_0900_one2one_bi_pk

b) @primaryKeyJoinColumn(不常用,瞭解)

c) xml: <one-to-one id 使用foreign class<one-to-one property-ref

聯合主鍵

a) 項目名稱hibernate_1000_one2one_uni_fk_composite

b) @JoinColumns

Wife類中建立聯合主鍵,建立方式參考 ID生成策略中的聯合主鍵部分

Husband類中寫Wife對象屬性,並在其get方法上寫@OneToOne即可完成一對一外鍵映射

若想要指定生成的外鍵名 則需使用@JoinColumns註解,如下:

@OneToOne

@JoinColumns( { @JoinColumn(name = "wifeid", referencedColumnName = "id"),

@JoinColumn(name = "wifename", referencedColumnName = "name") })

/*@JoinColumns用於在一對一外鍵關聯存在聯合主鍵情況時指定生成的外鍵字段名稱

@JoinColumns的參數爲@JoinColumn數組 @JoinColumn內除需指定name屬性外還需指定 

referencedColumnName屬性值 作用是可指定生成的字段名所對應的目標表字段名*/

public Wife getWife() {……}

組件映射

項目:hibernate_1100_component

對象關係:一個對象另外一個對象的一部分

數據庫表:一張表

annotation: @ Embeddable @Embbeded

       對象模型

Husband(id,name,wife)

Wife(name,age)

Annotation:

Husbandwife屬性上建立註解

@Embedded 表明該對象是從別的位置嵌入過來的,是不需要單獨映射的表.

這種方式生成的表爲husband(id,name,wifename,wifeage),不會生成wife.

@Embedded

Public Wift getWife(){

}

@AttributeOverride註解需要寫在getWife方法上,可以重新指定生成的Wife類組件生成的字段名,例如:Husband與Wife兩個類中都有name字段,這樣在生成表的時候會有衝突,此時採用@AttributeOverride註解可以指定Wife類中的name屬性對應新的字段名—“wifename,不過@AttributeOverride註解不常用,因爲有更好的解決方法. 1:不要在組件的兩個映射類中寫同名屬性;2:如果真的有重複,那麼可以在分類中(此處爲Wife類)的重複名稱的屬性上使用如下內容以指定新的字段名:

@Column(name="wifename")

public String getName() {

return name;

}

另外,@ Embeddable註解好像是寫在分類(Wife類)的類名前的,不過好像不寫也行

@Embeddable

public class Wife {… }

xml: 使用<component,例如:

     

<class name="Husband" >

<id name="id">

<generator class="native"/>

</id>

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

<component name="wife">

<property name="wifeName"/>

<property name="wifeAge"/>

</component>

</class>

多對一與一對多

多對一單向關聯

a) 項目名稱:hibernate_1200_many2one_uni

實體模型(User多對一Group)

User(id,name,group)

Group(id,name)

b) 數據庫表設計:在多方加

錯誤做法:在一方加冗餘

perosnid

person name

dreamid

1

zhangsan

1

1

zhangsan

2

dreamid

dreamdescr

1

earn money

2

eat a lot

c) annotaion: @Many2One

只需要在多的一端User屬性group進行註解配置

@ManyToOne //多對一關聯 User是多的一方 Group是一的一方

@JoinColumn(name="groupid") //指定User表中生成與Group對應的字段名

public Group getGroup() {

return group;

}

d) xml: <many-to-one

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

標籤會在的一端添加外鍵,相當於在數據庫中添加外鍵

生成的表爲user(id,name,groupid),t_group(id,groupname)

屬性cascade

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

取值all,none,save-update,delete,對象間的級聯操作,只對增刪改起作用.

在存儲時User,設置了cascade="all"會自動存儲相應的t_group.而不用管user關聯的對象(通常情況下會優先存儲關聯的對象,然後再存儲user).

一對多單向關聯

a) 項目名稱:hibernate_1300_one2many_uni

模型(group一對多user)

Group(id,name,users)

User(id,name)

設計時在一的這一端存在着多的集合,生成的數據庫表通常是在多的一端生成外鍵.

Set<User> users = new HashSet<User>();

b) 類:在一的一方存在多方的集合

c) 數據庫表同上

d) Annotation:@One2Many

在一的這一端Groupusers屬性上進行註解配置

@OneToMany  //一對多關聯 Group是一的一方 User是多的一方 

@JoinColumn(name="groupid")  //指定User表中生成與Group對應的字段名 注意此處與多對一配置方式不同

public Set<User> getUsers(){ ……. }

Hibernate默認將OneToMany理解爲ManyToMany的特殊形式,如果不指定生成的外鍵列@JoinColumn(name="groupId"),則會默認生成多對多的關係,產生一張中間表

e) xml:<set <one2many

XML配置中配置一的那一端Group

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

<id name="id">

<generator class="native"/>

</id>

<property name="name"/>

<set name="users">

<key column="groupId"/>指定生成外鍵字段的名字

<one-to-many class="com.pojo.User"/>

</set>

</class>

一對多(多對一)雙向關聯

一對多與多對一的雙向關聯是同一種情況.

關係模型(group一對多user)

Group(id,name,users)

User(id,name,group)

Set<User> users=new HashSet<User>()

配置規則:一般以多的一端爲主,先配置多的一端

在多的一端User端配置group

@ManyToOne

@JoinColumn(name="groupid")

在一的一端Group端配置時,users只需要加個mappedBy="groupid"

@OneToMany(mappedBy="group")

XML配置

Group

<set name="users">

<key column="groupId"/>

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

</set>

User

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

務必確保在多的一端生成的生成的外鍵和一的一方生成的外鍵的名字相同,都爲groupId.

如果名字不同則會在多的一端生成多餘的外鍵

多對多

單向關聯:

a) 項目:hibernate_1500_many2many_uni

關係模型(Teache多對多Student),Teacher這一端能關聯到students.

Teacher(id,name,students)

Student(id,name)

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

Teacher那一端配置

b) 例如:老師和學生的關係,老師需要知道自己教了哪些學生

c) 數據庫:生成中間表

d) Annotation@Many2Many

Teacher類中寫:

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

@ManyToMany  //多對多關聯 Teacher是主的一方 Student是附屬的一方 

@JoinTable(

name="t_s",  //指定中間表表名

      joinColumns={@JoinColumn(name="teacherid")},//本類主鍵在中間表生成的對應字段名

      inverseJoinColumns={@JoinColumn(name="studentid")}//對方類主鍵在中間表生成的對應字段名

    )

public Set<Student> getStudents(){……}

e) XML<many2many

<class name="com.xxx.Teacher">

<id name="id">

<generator class="native"/>

</id>

<property name="name"/>

<set name="students" table="t_s">table定義中間表的表名

<key column="teacher_id"></key>

<many-to-many class="com.xxx.Student" column="student_id"/>

</set>

</class>

雙向關聯:

a) 項目hibernate_1600_many2many_bi

多對多向配置只需要在進行配置就可以.

關係模型(Teache多對多Student)

Teacher(id,name,students)

Student(id,name,teachers)

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

Set<Teacher> teachers = new HashSet<Teacher>();

b) 老師知道自己教了哪學生學生知道教自己的有哪些老師

c) 數據庫:生成中間表

d) Annotation

Teacher這一端的students上配置

@ManyToMany

@JoinTable(name="t_s",

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

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

)

Student一端的teachers只需要配置

@ManyToMany(mappedBy="students")

注意:mappedBy 與 @JoinTable等一類的配置要分開,不然表字段可能亂

e) XML

XML配置方式:兩端配置一樣,注意表名和生成的中間表的字段屬性名要一致

Teacher那一端配置

<set name="students" table="t_s">

<key column="teacher_id"/>

<many-to-many class="com.xxx.Student" column="student_id"/>

</set>

Student那一端配置

<set name="teachers" table="t_s">

<key column="student_id"></key>

<many-to-many class="com.xxx.Teacher" column="teacher_id"/>

</set>

生成的數據庫表和上面是一樣的

關聯關係中的CRUD_Cascade_Fetch

hibernate_1700_one2many_many2one_bi_crud

設定cascade以設定在持久化時對關聯對象的操作(CUDRFetch管)

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

a) Cascade的屬性是數組格式,指明做什麼操作的時候關聯對象是綁在一起的

b) refresh = A裏面需要讀B改過之後的數據

cascade={CascadeType.ALL} 

CascadeType取值

ALL    Cascade all operations所有情況

MERGE  Cascade merge operation合併(merge=save+update)

PERSIST  Cascade persist operation存儲 persist()

REFRESH  Cascade refresh operation刷新

REMOVE   Cascade remove operation刪除

鐵律:雙向關係在程序中要設定雙向關聯

鐵律:雙向mappedBy

fetch

a) 鐵律:雙向不要兩邊設置Eager(會有多餘的査詢語句發出)

b) 對多方設置fetch的時候要謹慎,結合具體應用,一般用Lazy不用eager,特殊情況(多方數量不多的時候可以考慮,提高效率的時候可以考慮)

@OneToMany(mappedBy="group",

cascade={CascadeType.ALL}, //控制增刪改(CUD)

fetch=FetchType.EAGER //控制查詢(R) EAGER值代表取出關聯 LAZY值爲不取關聯 

 //多的一方fetch取值默認爲LAZY 一的一方默認爲EAGER

)

另外:如果User類(即多的一方)中設置fetch=FetchType.LAZY 則在調用多(即Group)的對象值的時候 

類似延遲加載 即需要在commit();之前 session還存在時調用 如:

System.out.println(user.getGroup().getName());  

session.getTransaction().commit();

Update@ManyToOne()中的cascade參數關係

session.beginTransaction();

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

//user對象屬性改變 事務commit時自動判斷與數據庫原有數據不同 可自動update

//時的update@ManyToOne()中的cascadefetch參數取值無關

user.setName("user1");

user.getGroup().setName("group1");

session.getTransaction().commit();

如果user改變在commit()之後 且想要執行Update方法時 usergroup表同時更新則,則User類的cascade={CascadeType.ALL},並在程序中寫如下代碼:

session.beginTransaction();

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

session.getTransaction().commit();

user.setName("user1");

user.getGroup().setName("group1");

Session session2 = sessionFactory.getCurrentSession();

session2.beginTransaction();

session2.update(user);

session2.getTransaction().commit();

Delete@ManyToOne()中的cascade關係

如果UserGroup類中均設爲@ManyToOne(cascade={CascadeType.All})那麼在執行如下:

session.beginTransaction();

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

session.delete(user);

session.getTransaction().commit(); 

注意:此處刪除的是 多對一(UserGroup) 中的“多”的一方(User)

會刪除useruser對應的group,再反向對應groupuser都會刪除,原因就是設置了@ManyToOne(cascade={CascadeType.All})

三種方法可避免全部刪除的情況:

1. 去掉@ManyToOne(cascade={CascadeType.All})設置;

2. 直接寫Hql語句執行刪除;

3. user對象的group屬性設爲null,相當於打斷UserGroup間的關聯,代碼如下

session.beginTransaction();

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

user.setGroup(null);

session.delete(user);

session.getTransaction().commit();

注意:如果刪除的是 多對一中的“一”的一方(Group)時,如果使用第3種方式(user屬性設爲null)來打斷兩個對象間的關聯的話,代碼與之前不同,如下:

session.beginTransaction();

Group group = (Group)session.load(Group.class,1);

//循環將group中的set集合下的各個user對象設爲null

//相當於先將數據庫中user表中與group表關聯的字段(groupid)設爲null

for(User user :group.getUsers()){

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

user.setGroup(null);

}

//再將groupset集合設爲null,相當於將group表中與user表關聯的字段(userid)設爲null

//此句的前提是user表中的關聯字段(groupid)已經爲null,如沒有相當於破壞了一對多關聯,會報錯

group.setUsers(null);

session.delete(group);

session.getTransaction().commit();

O/RMapping 編程模

a) 映射模型

i. jpa annotation(java提供的annotation配置--常用)

ii. hibernate annotation extension(Hibernate擴展的annotation配置--較少用)

iii. hibernate xml(Hibernatexml配置方式--常用)

iv. jpa xml(java提供的xml配置--較少用)

b) 編程接口

i. Jpa(不常用)

ii. hibernate(現在用)

c) 數據査詢語言

i. HQL

ii. EJBQL(JPQL)

10 要想刪除或者更新先做load,除了精確知道ID之外

11 如果想消除關聯關係,先設定關係爲null.再刪除對應記錄,如果不刪記錄,該記錄變成垃圾數據

12 練習:多對多的CRUD

teacher

student

t1

s1

t1

s2

t2

s1

t2

s2

關係映射總結

什麼樣的關係,設計什麼樣的表,進行什麼樣的映射

CRUD,按照自然的理解即可(動手測試)

集合映射(不太重要)

項目名稱:hibernate_1800_Collections_Mapping

Set

List (Set差不多 多個@OrderBy)

a) @OrderBy

Map

a) @Mapkey

繼承映射(不太重要)

三種方式

a) 一張總表SINGLE_TABLE

i. hibernate_1900_lnheritence_Mapping_Single_Table

b) 每個類分別一張表TABLE_PER_CLASS

i. hibernate_2000_lnheritence_Mapping_Table_Per_Class

c) 每個子類一張表jOINED

i. hibernate_2100_lnheritence_Mapping_JOINED

父類上加註解@Inheritance(strategy=InheritanceType.JOINED)

@Inheritance(strategy=InheritanceType.JOINED)

public class Person {... ...}

作業:

學生課程、分數的設計(重要) 

a) 使用聯合主鍵@Embeddedld

i. 實現 Serializable 接口

b) 不使用聯合主鍵(視頻中實際例子採用此類方法)

注:自己的項目與馬老的方式不同,但可實現同樣的功能(除從學生查課程外),具體見項目hibernate_2300_Stu_Course_Score

設計:

a) 實體類(表)

b) 導航(編程方便)

c) 確定了編程方式

樹狀結構的設計至關重要

a) 在同個類中使用One2ManyMany20ne

關係模型(Tree)

Tree(int id,String name,Tree parent,List children)

private List<Tree> children = new ArrayList<Tree>(); 

@Id

    @GeneratedValue

    public int getId(){ 

             return id; 

     }

@ManyToOne

   @JoinColumn(name="parent_id")

   public Tree getParent(){ 

           return parent; 

   }

//fetch=FetchType.EAGER可省略 即爲@OneToMany的默認值fetch=FetchType.LAZY

     //若樹形較小 可使用EAGER 一次全部載入內存

//若爲LAZY則查詢樹形時不會一次全部載入內存(適用於較大的樹形),會每取一個葉子節點就select

一次

     @OneToMany(mappedBy="parent",

     cascade={CascadeType.ALL},

     fetch=FetchType.EAGER   )

     public List<Tree> getChildren() {

return children;

}

Hibernate 查詢(Query Language)

HQL vs EJBQL

NativeSQL >HQL.> EJBQL(JPQL 1.0) > QBC(Query By Criteria) > QBE(Query By Example)"

總結:QL應該和導航關係結合,共同爲査詢提供服務。

性能優化

注意session.clear()的運用,尤其在不斷分頁循環的時候

a) 在一個大集合中進行遍歷,遍歷msg,取出其中的含有敏感字樣的對象

b) 另外一種形式的內存泄露  (  //面試題:Java有內存泄漏嗎?語法級別沒有 但是可由java引起,例如:連接池不關閉,io讀取後不關閉)

1+N問題 (典型的試題) (詳見hibernate_2800_Hibernate_1+N項目)

a) @ManyToOne(fetch=FetchType.LAZY) 

//fetch=FetchType.LAZY 解決N+1問題 說明如下:

//當多對一(@ManyToOne)已經設定屬性" fetch=FetchType.LAZY "時 

//只有當需要時(:t.getCategory().getName())纔會去獲取關聯表中數據 可以解決N+1問題

b) @BatchSize

//@BatchSize 解決N+1問題 說明如下:

//在與查詢表(此例中爲Topic)關聯的表類(此例中爲Category)頭處加@BatchSize(size=5)

//表示每次可查出5條記錄 從而減少了select語句的個數

c) join fetch

//join fetch 解決N+1問題 說明如下:

//修改hql語句爲--"  from Topic t left join fetch t.category c  "

d) QBC

//QBC(Query By Criteria) 解決N+1問題 說明如下:

//使用QBC createCriteria(*.class)執行查詢 也可避免N+1問題

listiterate不同之處(//主要爲了面試  詳見hibernate_2900_Hibernate_list_iterate

a) list取所有

b) iterate先取 ID,等用到的時候再根據ID來取對象

c) sessionlist第二次發出,仍會到數據庫査詢

d) iterate 第二次,首先找session 級緩存

一級緩存和二級緩存和査詢緩存(面試題)(詳見hibernate_3000_Hibernate_3KindsOf_Cache)

a) 什麼是緩存

b) 什麼是一級緩存,session級別的緩存

c) I什麼是二級緩存,SessionFactory級別的緩存,可以跨越session存在

i. 經常被訪間

ii. 改動不大不會經常改動

iii. 數重有限

d) 打開二級緩存

i. hibernate.cfg.xml 設定:

<property

name= "cache.use_second_level_cache">true</property>

<property

name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

ii. @Cache註解(hibernate擴展提供)

@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)

注:使用EhCache二級緩存 需要導入ehcache-1.2.3.jarcommons-logging-1.0.4.jar

e) load默認使用二級緩存,iterate默認使用二級緩存

f) list默認往二級緩存加數據,但是查詢的時候不使用

g) 如果要query用二級緩存,需打開查詢緩存

<property name="cache.use_query_cache">true</property>

調用QuerysetCachable (true)方法指明使用二級緩存

例如:session.createQuery("from Category").setCacheable(true).list();

h) 緩存算法:(純爲了面試)

i. LRU  LF FIFO

1. Least Recently Used 最近很少被使用

2. Least Frequently Used (命中率高低)

3. First In First Out 按順序替換

ii. memoryStoreEvictionPolicy = "LRU" (ehcache.xml中配置

事務併發處理(面試的意義更大)

a) 事務ACID

i. Atomic Consistency Itegrity Durability

b) 事務併發時可能出現的問題:

第一類丟失更新(Lost Update) 

時間

取款事務A

存款事務B

T1

開始事務

T2

開始事務

T3

查詢賬戶餘額爲1000

T4

查詢賬戶餘額爲1000

T5

匯入100元把餘額改爲1100

T6

提交事務

T7

取出100元把餘額改爲900 

T8

撤銷事務

T9

餘額恢復爲1000(丟失更新)

 

dirty read髒讀(讀到了另一個事務在處理中還未提交的數據)

時間

取款事務A

存款事務B

T1

開始事務

T2

開始事務

T3

查詢賬戶餘額爲1000

T4

匯入100元把餘額改爲1100

T5

查詢賬戶餘額爲1100(讀取髒數據)

T6

回滾

T7

取款1100

T8

提交事務失敗

non-repeatable read 不可重複讀

時間

取款事務A

存款事務B

T1

開始事務

T2

開始事務

T3

查詢賬戶餘額爲1000元

T5

匯入100元把餘額改爲1100元

T5

提交事務

T6

查詢帳戶餘額爲1100元

T8

提交事務

second lost update problem 第二類丟失更新(不可重複讀的特殊情況)

時間

取款事務A

存款事務B

T1

開始事務

T2

開始事務

T3

查詢賬戶餘額爲1000元

T4

查詢賬戶餘額爲1000元

T5

取出100元把餘額改爲900元

T6

提交事務

T7

匯入100元

T8

提交事務

T9

把餘額改爲1100(丟失更新)

phantom read 幻讀

時間

查詢學生事務A

插入新學生事務B

T1

開始事務

T2

開始事務

T3

查詢學生爲10人

T4

插入1個學生

T5

查詢學生爲11人

T6

提交事務

T7

提交事務

c) 數據庫的事務隔離機制

i. 查看 java.sql.Connection 文檔

ii. 1read-uncommitted  2read-committed  4repeatable read  8serializable(數字代表對應值)

爲什麼取值要使用 1 2 4 8 而不是 1 2 3 4

1=0000  2=0010 4=0100 8=1000(位移計算效率高)

1. 只要數據庫支持事務,就不可能出現第一類丟失更新

2. read-uncommitted(允許讀取未提交的數據) 會出現dirty read, phantom-read, 

non-repeatable read 

3. read-commited(讀取已提交的數據 項目中一般都使用這個)不會出現dirty read,因爲只有另

一個事務提交纔會讀出來結果,但然會出現 non-repeatable read  phantom-read

使用read-commited機制可用悲觀鎖 樂觀鎖來解決non-repeatable read  phantom-read問題

4. repeatable read(事務執行中其他事務無法執行修改或插入操作     較安全)

5. serializable解決一切問題(順序執行事務 不併發,實際中很少用)

d) 設定hibernate的事務隔離級別(使用hibernate.connection.isolation配置 取值1、2、4、8)

i. hibernate.connection.isolation = 2如果不設 默認依賴數據庫本身的級別

ii. 用悲觀鎖解決repeatable read的問題(依賴於數據庫的鎖)

(詳見項目 hibernate_3100_Hibernate_Concurrency_Pessimistic_Lock)

1. select ... for update

2. 使用另一種load方法--load(xx.class , i , LockMode.Upgrade)

a) LockMode.None無鎖的機制,Transaction結束時,切換到此模式

b) LockMode.read在査詢的時候hibernate會自動獲取鎖

c) LockMode.write insert  update hibernate 會自動獲取鎖

d) 以上3種鎖的模式,是hibernate內部使用的(不需要設)

e) LockMode.UPGRADE_NOWAIT ORACLE 支持的鎖的方式

e) Hibernate(JPA)樂觀鎖定(ReadCommitted)

(詳見項目hibernate_3200_Hibernate_Concurrency_Optimistic_Lock)

實體類中增加version屬性(數據庫也會對應生成該字段,初始值爲0),並在其get方法前加

@Version註解,則在操作過程中沒更新一次該行數據則version值加1,即可在事務提交前判斷該數據是否被其他事務修改過.

@Version

時間

轉賬事務A

取款事務B

T1

開始事務

T2

開始事務

T3

查詢學生爲10

查詢賬戶餘額爲1000 version=0

T4

查詢賬戶餘額爲1000 version=0

T5

取出100 把餘額改爲900 version=1

T6

提交事務

T7

匯入100

T8

提交事務 ? version>0 

throw Exception

T9

把餘額改爲1100元(丟失更新)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章