一、 HelloWorldjava
a) Xmlnode
b) Annotctionmysql
二、 hibernate原理模擬-什麼是O/RMapping以及爲何要有O/RMappingweb
三、 常風的O/R框架面試
四、 Hibernate基礎配置算法
五、 Hibernate核心接口介紹spring
六、 對象的三種狀態sql
七、 ID生成策略數據庫
八、 關係映射
九、 Hibernate查詢(HQL)
十、 在Struts基礎上繼續完美BBS2009
十一、 性能優化
十二、 補充話題
一、 先脈絡,後細節
二、 先操做、後原理
三、 重Annotation,輕xml配置文件
a) JPA (能夠認爲EJB3的一部分)
b) Hibernate– extension
a) hibernate-distribution-3.3.2.GA-dist.zip
b) hibernate-annotations-3.4.0.GA.zip
c) slf4j-1.5.10.zip (hibernate內部日誌用)
二、 hibernatezh_CN文檔
三、 hibernateannotateon references
一、 下載hibernate3.3.2
二、 下載hibernate-annotations-3.4.0
三、 注意閱讀hibernate compatibility matrix
四、 下載slf4j 1.5.8
一、 創建新的Java項目,名爲hibernate_0100_HelloWorld
二、 學習創建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
三、 引入MySQL的JDBC驅動名
a) Create databasehibernate;
b) Usehibernate;
c) Createtable Student (id int primary key, name varchar(20),age int);
五、 創建hibernate配置文件hibernate.cfg.xml
a) 從參考文檔中copy
b) 修改對應的數據庫鏈接
c) 註釋提暫時不須要的內容
六、 創建Student類
七、 創建Student映射文件Student.hbm.xml
a) 參考文檔
八、 將映射文件加入到hibernate.cfg.xml
a) 參考文檔
九、 寫測試類Main,在Main中對Student對象進行直接的存儲測試
一、 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裏的操做都應該放在事務裏
注意:要求hibernate3.0版本之後支持
一、 建立teacher表,create table teacher(id int primary key,namevarchar(20),title varchar(10));
二、 建立Teacher類
一、 在hibernate library中加入annotation的jar包
a) Hibernateannotations jar
b) Ejb3persistence jar
c) Hibernatecommon annotations jar
d) 注意文檔中沒有提到hibernate-common-annotations.jar文件
二、 參考Annotation文檔創建對應的註解
一、 在hibernate.cfg.xml中創建映射<maping class=…/>
<mapping class="com.wjt276.hibernate.model.Teacher"/>
注意:<mapping>標籤中使用的是class屬性,而不是resource屬性,而且使用小數點(.)導航,而不是」/」
二、 參考文檔進行測試
一、 FAQ:@ 後不給提示
解決方法:windows→Proferences→seach 「Content Assist」設置Auto-Activation以下:
——————————————————————————————————————————————————————————————————————————————————————————————
ORM(ObjectRelational Mapping)---是一種爲了解決面向對象與關係型數據庫存在的互不匹配的現象的技術。簡單說:ORM是經過使用描述對象和數據庫之間映射的元數據,將Java程序中的對象自動持久化到關係數據中。本質上就是將數據從一種形式轉換到另一種形式。
分層後,上層不須要知道下層是如何作了。
分層後,不能夠循環依賴,通常是單向依賴。
Gavin King
一、 就是將對象模型(實體類)的東西存入關係模型中,
二、 實體中類對應關係型庫中的一個表,
三、 實體類中的一個屬性會對應關係型數據庫表中的一個列
四、 實體類的一個實例會對應關係型數據庫表中的一條記錄。
%%將對象數據保存到數據庫、將數據庫數據讀入到對象中%%
OOA---面向對象的分析、面向對象的設計
OOD---設計對象化
OOP---面向對象的開發
阻抗不匹配---例JAVA類中有繼承關係,但關係型數據庫中不存在這個概念這就是阻抗不匹配。Hibernate能夠解決這個問題
一、 解決阻抗不匹配的問題;
二、 目前不存在完整的面向對象的數據庫(目前都是關係型數據庫);
三、 JDBC操做數據庫很繁瑣
四、 SQL語句編寫並非面向對象
五、 能夠在對象和關係表之間創建關聯來簡化編程
六、 O/RMapping簡化編程
七、 O/RMapping跨越數據庫平臺
八、 hibernate_0200_OR_Mapping_Simulation
一、 不須要編寫的SQL語句(不須要編輯JDBC),只須要操做相應的對象就能夠了,就能夠可以存儲、更新、刪除、加載對象,能夠提升生產效;
二、 由於使用Hibernate只須要操做對象就能夠了,因此咱們的開發更對象化了;
三、 使用Hibernate,移植性好(只要使用Hibernate標準開發,更換數據庫時,只須要配置相應的配置文件就能夠了,不須要作其它任務的操做);
四、 Hibernate實現了透明持久化:當保存一個對象時,這個對象不須要繼承Hibernate中的任何類、實現任何接口,只是個純粹的單純對象—稱爲POJO對象(最純粹的對象—這個對象沒有繼承第三方框架的任何類和實現它的任何接口)
五、 Hibernate是一個沒有侵入性的框架,沒有侵入性的框架咱們通常稱爲輕量級框架
六、 Hibernate代碼測試方便。
1. 針對某一個對象,簡單的將它加載、編輯、修改,且修改只是對單個對象(而不是批量的進行修改),這種狀況比較適用;
2. 對象之間有着很清晰的關係(例:多個用戶屬於一個組(多對一)、一個組有多個用戶(一對多));
3. 彙集性操做:批量性添加、修改時,不適合使用Hibernate(O/映射框架都不適合使用);
4. 要求使用數據庫中特定的功能時不適合使用,由於Hibernate不使用SQL語句;
l 屬性映射;
l 類映射:
l 關聯映射:
n 一對一;
n 一對多;
n 多對多。
一、 Hibernate
二、 ApacheOJB
三、 JDO(是SUN提出的一套標準—Java數據對象)
四、 Toplink(Orocle公司的)
五、 EJB(2.0X中有CMP;3.0X提出了一套「Java持久化API」---JPA)
六、 IBatis(很是的輕量級,對JDBC作了一個很是很是輕量級的包裝,嚴格說不是O/R映射框架,而是基於SQL的映射(提供了一套配置文件,把SQL語句配置到文件中,再配置一個對象進去,只要訪問配置文件時,就可獲得對象))
七、 JAP(是SUN公司的一套標準)
a) 意願統一天下
咱們使用一個項目來完成
功能:有一個配置文件,文件中完成表名與類名對象,字段與類屬性對應起來。
測試驅動開發
hibernate_0200_OR_Mapping_Simulation
一、 對應項目:hibernate_0300_BasicConfiguration
二、 介紹MYSQL的圖形化客戶端
三、 Hibernate.cfg.xml:hbm2ddl.auto
a) 先建表仍是先建實體類
四、 搭建日誌環境並配置顯示DDL語句
五、 搭建Junit環境
a) 須要注意Junit的Bug
六、 ehibernate.cfg.xml: show_sql
七、 hibernate.cfg.xml:format_sql
八、 表名和類名不一樣,對錶名進行配置
a) Annotation:@Table
b) Xml:本身查詢
九、 字段名和屬性相同
a) 默認爲@Basic
b) Xml中不用寫column
十、 字段名和屬性名不一樣
a) Annotation:@Column
b) Xml:本身查詢
十一、 不須要psersistence的字段
a) Annotation:@Transient
b) Xml:不寫
十二、 映射日期與時間類型,指定時間精度
a) Annotation:@Temporal
b) Xml:指定type
1三、 映射枚舉類型
a) Annotation:@Enumerated
b) Xml:麻煩
1四、 字段映射的位置(field或者get方法)
a) Best practice:保持field和get/set方法的一致
1五、 @Lob
1六、 課外:CLOB BLOB類型的數據存取
1七、 課外:Hibernate自定義數據類型
1八、 Hibernate類型
這樣的軟件網絡不少,主要本身動手作
在SessionFactory建立時,自動檢查數據庫結構,或者將數據庫schema的DDL導出到數據庫. 使用 create-drop時,在顯式關閉SessionFactory時,將drop掉數據庫schema.取值 validate | update | create | create-drop
咱們使用slf接口,而後使用log4j的實現。
一、 首先引入log4j的jar包(log4j-1.2.14.jar),
二、 而後再引入slf4j實現LOG4J和適配器jar包(slf4j-log4j12-1.5.8.jar)
三、 最後建立log4j的配置文件(log4j.properties),並加以修改,只要保留
log4j.logger.org.hibernate.tool.hbm2ddl=debug
一、首先引入Junit 類庫 jar包 (junit-4.8.1.jar)
二、在項目名上右鍵→new→Source Folder→輸入名稱→finish
三、注意,你對哪一個包進行測試,你就在測試下創建和那個包相同的包
四、創建測試類,須要在測試的方法前面加入」@Test」
輸出全部SQL語句到控制檯. 有一個另外的選擇是把org.hibernate.SQL這個log category設爲debug。
取值: true | false
在log和console中打印出更漂亮的SQL。
取值: true | false
True樣式:
False樣式:
Annotation:使用 @Table(name=」tableName」) 進行註解
例如:
Xml:
Annotation:默認爲@Basic
注意:若是在成員屬性沒有加入任何註解,則默認在前面加入了@Basic
Xml中不用寫column
Annotation:使用@Column(name=」columnName」)進行註解
例如:
Xml:
就是不實體類的某個成員屬性不須要存入數據庫中
Annotation:使用@Transient 進行註解就能夠了。
例如:
Xml:不寫(就是不須要對這個成員屬性進行映射)
Annotation:使用@Temporal(value=TemporalType)來註解表示日期和時間的註解
其中TemporalType有三個值:TemporalType.TIMESTAMP 表示yyyy-MM-dd HH:mm:ss
TemporalType.DATE 表示yyyy-MM-dd
TemporalType.TIME 表示HH:mm:ss
注意:當使用註解時,屬性爲value時,則這個屬性名能夠省略,例如:@Temporal(TemporalType)
Xml:使用type屬性指定hibernate類型
注意:hibernate日期時間類型有:date, time, timestamp,固然您也可使用Java包裝類
Annotation:使用@Enumerated(value=EnumType)來註解表示此成員屬性爲枚舉映射到數據庫
其中EnumType有二個值:①EnumType.STRING 表示直接將枚舉名稱存入數據庫
②EnumType.ORDINAL 表示將枚舉所對應的數值存入數據庫
Xml:映射很是的麻煩,先要定義自定義類型,而後再使用這個定義的類型……
(也就是實體類生成數據庫中的表),完整代碼以下:
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(true, true);
}}
運行剛剛創建的ExportDB類中的main()方法,進行實際的導出類。
<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>定義能夠訪問舊式的多主鍵數據。 咱們強烈不建議使用這種方式。
主鍵生成策略是必須配置
用來爲該持久化類的實例生成惟一的標識。若是這個生成器實例須要某些配置值或者初始化參數, 用<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>聯合起來使用。
使用@GeneratedValue(strategy=GenerationType)註解能夠定義該標識符的生成策略
Strategy有四個值:
① 、AUTO- 能夠是identity column類型,或者sequence類型或者table類型,取決於不一樣的底層數據庫.
至關於native
② 、TABLE- 使用表保存id值
③ 、IDENTITY- identity column
④ 、SEQUENCE- sequence
注意:auto是默認值,也就是說沒有後的參數則表示爲auto
@Id
@GeneratedValue
public int getId() {
return id;
}
或
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return id;
}
一、 對於mysql,使用auto_increment
二、 對於oracle使用hibernate_sequence(名稱固定)
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return id;
}
對DB2,MySQL, MS SQL Server,Sybase和HypersonicSQL的內置標識字段提供支持。 返回的標識符是long, short 或者int類型的。 (數據庫自增)
注意:今生成策略不支持Oracle
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
public int getId() {
return id;
}
在DB2,PostgreSQL,Oracle, SAP DB, McKoi中使用序列(sequence), 而在Interbase中使用生成器(generator)。返回的標識符是long, short或者 int類型的。(數據庫自增)
注意:今生成策略不支持MySQL
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;
}}
原理:就是在數據庫中創建一個表,這個表包含兩個字段,一個字段表示名稱,另外一個字段表示值。每次在添加數據時,使用第一個字段的名稱,來取值做爲添加數據的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三個屬性值同樣就能夠了。),這個主鍵生成策略能夠跨數據庫平臺。
複合主鍵(聯合主鍵):多個字段構成惟一性。
// 覈算期間
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()方法(複寫方法以下圖片),而後在類中引用這個複合主鍵類
複合主鍵必需實現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;
}}
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;
}
………………
<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>
create table t_fiscalYearPeriod (fiscalYear integer not null, fiscalPeriodinteger not null, beginDate datetime, endDate datetime, periodSts varchar(255),primary key (fiscalYear, fiscalPeriod))//實體映射到數據就是兩個字段構成複合主鍵
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);
Hibernate: insert into t_fiscalYearPeriod (beginDate, endDate, periodSts,fiscalYear, fiscalPeriod) values (?, ?, ?, ?, ?)
注:若是再存入相同複合主鍵的記錄,就會出錯。
數據加載很是簡單,只是主鍵是一個對象而以,不是一個普通屬性。
下面是定義組合主鍵的幾種語法:
將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id
將組件的屬性註解爲@EmbeddedId
將類註解爲@IdClass,並將該實體中全部屬於主鍵的屬性都註解爲@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;
}}
注意:只須要在實體類中表示覆合主鍵屬性前註解爲@Entity,表示此主鍵是一個複合主鍵
注意了,複合主鍵類不須要任何的注意。
@Entity
public class Teacher {
private TeacherPK pk;
private String title;
@EmbeddedId
public TeacherPK getPk(){
return pk;
}}
須要將複合主鍵類創建好,不須要進行任何註解
在實體類中不須要進行復合主鍵類的引用
須要在實體類前面註解爲@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;
}}
做用:進行配置信息的管理
目標:用來產生SessionFactory
能夠在configure方法中指定hibernate配置文件,默認(不指定)時在classpath下加載hibernate.cfg.xml文件
加載默認的hibernate的配置文件
sessionFactory = newAnnotationConfiguration().configure().buildSessionFactory();
加載指定hibernate的配置文件
sessionFactory=newnnotationConfiguration().configure("hibernate.xml").buildSessionFactory();
只須要關注一個方法:buildSessionFactory();
做用:主要用於產生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.save(Object)
session的save方法是向數據庫中保存一個對象,這個方法產生對象的三種狀態
session.delete(Object)
Object對象須要有ID
對象刪除後,對象狀態爲Transistent狀態
格式: 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();
}
}
}
格式: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();
}
}
①、 不存在對應記錄時表現不同;
②、 load返回的是代理對象,等到真正使用對象的內容時才發出sql語句,這樣就要求在第一次使用對象時,要求session處於open狀態,不然出錯
③、 get直接從數據庫加載,不會延遲加載
get()和load()只根據主鍵查詢,不能根據其它字段查詢,若是想根據非主鍵查詢,可使用HQL
① 、用來更新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();
}
在執行的時候hibernate會檢查,若是對象在數據庫中已經有對應的記錄(是指主鍵),則會更新update,不然會添加數據save
清除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到緩存中 了。
在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語句。
例如: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();
使用new操做符初始化的對象不是馬上就持久的。它們的狀態是瞬時的,也就是說它們沒有任何跟數據庫表相關聯的行爲,只要應用再也不引用這些對象(再也不被任何其它對象所引用),它們的狀態將會丟失,並由垃圾回收機制回收
持久實例是任何具備數據庫標識的實例,它有持久化管理器Session統一管理,持久實例是在事務中進行操做的----它們的狀態在事務結束時同數據庫進行同步。當事務提交時,經過執行SQL的INSERT、UPDATE和DELETE語句把內存中的狀態同步到數據庫中。
Session關閉以後,持久化對象就變爲離線對象。離線表示這個對象不能再與數據庫保持同步,它們再也不受hibernate管理。
一、 有沒有ID,(若是沒有則是Transient狀態)
二、 ID在數據庫中有沒有
三、 在內存裏有沒有(session緩存)
Transient對象:隨時可能被垃圾回收器回收(在數據庫中沒有於之對應的記錄,應爲是new初始化),而執行save()方法後,就變爲Persistent對象(持久性對象),沒有歸入session的管理
內存中一個對象,沒有ID,緩存中也沒有
Persistent對象:在數據庫有存在的對應的記錄,歸入session管理。在清理緩存(髒數據檢查)的時候,會和數據庫同步。
內存中有、緩存中有、數據庫有(ID)
Detached對象:也可能被垃圾回收器回收掉(數據庫中存在對應的記錄,只是沒有任何對象引用它是指session引用),注引狀態通過Persistent狀態,沒有歸入session的管理
內存有、緩存沒有、數據庫有(ID)
注意:這裏的關係是指:對象之間的關係,並非指數據庫的關係,-----紅色重要
存在如下關係:
一、 一對一
u 單向(主鍵、外鍵)
u 雙向(主鍵、外鍵)
二、 一對多
u 單向
u 雙向
三、 多對一
u 單向
u 雙向
四、 多對多
u 單向
u 雙向
五、 集合映射
u List
u Set
u Map
六、 繼承關係(不重要)
u 單表
u 多表
u 一張主表、多張子表
七、 組件映射
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;}
}
人—-> 身份證號(PersonàIdCard),從IdCard看不到Person對象
須要在Person類中持有IdCard的一個引用idCard,則IdCard中沒有Person的引用
關係模型目的:是實體類映射到關係模型(數據庫中),是要求persion中添加一個外鍵指向idcard
注: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;}
}
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值
注意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;}
}
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字段
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();
人<—-> 身份證號(Person<->IdCard)雙向:互相持有對方的引用
關係模型沒有任務變化,同單向
實體類,只是相互持有對象的引用,而且要求getter和setter方法
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"/>
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;}
}
由於關係模型沒有變化,也就是數據庫的結構沒有變化,只是在數據加載時須要相互加載對方,這由hibernate來完成。由於生成的sql語句同單向同樣。
存儲同單向同樣
規律:凡是雙向關聯,必設mappedBy
主鍵關聯:即讓兩個對象具備相同的主鍵值,以代表它們之間的一一對應的關係;數據庫表不會有額外的字段來維護它們之間的關係,僅經過表的主鍵來關聯。
人—-> 身份證號(PersonàIdCard),從IdCard看不到Person對象
站在人的角度來看,對象模型與惟一外鍵關聯一個,只是關係模型不一樣
由於是person引用idcard,因此idcard要求先有值。而person的主鍵值不是本身生成的。而是參考idcard的值,person表中便是主鍵,同時也是外鍵
實體類同 一對一 惟一外鍵關聯的實體類一個,在person對象中持有idcard對象的引用(代碼見惟一外鍵關係)
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"/>
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實體類,不須要持有對象的引用,正常註解就能夠了。
生成的兩個表並無多餘的字段,由於是經過主鍵在關鍵的
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能夠映射,是主鍵關聯不重要
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();
讓兩個實體對象的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"/>
主鍵關聯:即讓兩個對象具備相同的主鍵值,以代表它們之間的一一對應的關係;數據庫表不會有額外的字段來維護它們之間的關係,僅經過表的主鍵來關聯。
主鍵關聯映射,實際是數據庫的存儲結構並無變化,只是要求雙方均可以持有對象引用,也就是說實體模型變化,實體類都相互持有對方引用。
另外映射文件也變化了。
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>
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;
}}
實現上聯合主鍵的原理同惟一外鍵關聯-單向同樣,只是使用的是@JoinColumns,而不是@JoinColumn,實體類註解以下:
@OneToOne
@JoinColumns(
{
@JoinColumn(name="wifeId", referencedColumnName="id"),
@JoinColumn(name="wifeName", referencedColumnName="name")
}
)
public WifegetWife() {
return wife;
}
注意:@oinColumns註解聯合主鍵一對一聯繫,而後再使用@JoinColumn來註解當前表中的外鍵字段名,並指定關聯哪一個字段,使用referencedColumnName指定哪一個字段的名稱
目前有兩個類以下:
你們發現用戶與員工存在不少相同的字段,可是二者有不能夠是同一個類中,這樣在實體類中每次都要輸入不少信息,如今把聯繫信息抽取出來成爲一個類,而後在用戶、員工對象中引用就能夠,以下:
值對象沒有標識,而實體對象具備標識,值對象屬於某一個實體,使用它重複使用率提高,並且更清析。
以上關係的映射稱爲component(組件)關聯映射
在hibernate中,component是某個實體的邏輯組成部分,它與實體的根本區別是沒有oid,component能夠成爲是值對象(DDD)。
採用component映射的好處:它實現了對象模型的細粒度劃分,層次會更加分明,複用率會更高。
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;}
}
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;}
}
<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>
使用@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類是值對象,不是實體對象,是屬於實體類的某一部分,所以沒有映射文件
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;}
}
實體類創建完後,開始建立映射文件,先創建簡單的映射文件:
<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>
<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>
例如:<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)
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;
}
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異常的一種手段。
級聯的意思是指定兩個對象之間的操做聯運關係,對一個 對象執行了操做以後,對其指定的級聯對象也須要執行相同的操做,取值:all、none、save_update、delete
一、 all:代碼在全部的狀況下都執行級聯操做
二、 none:在全部狀況下都不執行級聯操做
三、 save-update:在保存和更新的時候執行級聯操做
四、 delete:在刪除的時候執行級聯操做。
例如:<many-to-one name="group"column="groupid" cascade="save-update"/>
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;}
}
<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>
<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>
一對多 多的一端只須要正常註解就能夠了。
須要在一的一端進行註解一對多的關係。
使用@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;
}
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();
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();
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=?
是加載學生時,可以把班級加載上來。固然加載班級也能夠把學生加載上來
一、 在學生對象模型中,要持有班級的引用,並修改學生映射文件就能夠了。。
二、 存儲沒有變化
三、 關係模型也沒有變化
學生映射文件修改後的:
<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>
首先須要在多的一端持有一的一端的引用
由於一對多的雙向關聯,就是多對一的關係,咱們通常在多的一端來維護關係,這樣咱們須要在多的一端實體類進行映射多對一,而且設置一個字段,而在一的一端只須要進行映射一對多的關係就能夠了,以下:
多的一端
@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能夠被設置到集合標籤<set>上,默認inverse爲false,因此咱們能夠從一的一端和多的一端維護關聯關係,若是設置inverse爲true,則咱們只能從多的一端維護關聯關係。
注意:inverse屬性,隻影響數據的存儲,也就是持久化
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; }
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>
注意:由於是多對多單向(固然用戶擁有多個角色,一個角色也可屬性多個用戶,但這裏角色看不到用戶),因此角色只須要正常註解就能夠了,
如今要使用@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;}
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=?
商務主管
數據錄入人員
多對多關聯映射 雙向 兩方都持有對象引用,修改對象模型,但數據的存儲沒有變化。
再修改映射文件:
<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屬性值一致。
多對多關聯映射 雙向 兩方都持有對象引用,修改對象模型,但數據的存儲沒有變化
只須要修改註解映射就能夠了。
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=成龍
一、 設定cascade能夠設定在持久化時對於關聯對象的操做(CUD,R歸Fetch管)
二、 cascade僅僅是幫助咱們省了編程的麻煩而已,不要把它的做用看的太大
a) cascade的屬性指明作什麼操做的時候關係對象是綁在一塊兒的
b) refresh=A裏面須要讀B改過以後的數據
三、 鐵律:雙向關係在程序中要設定雙向關聯
四、 鐵律:雙向必定須要設置mappedBy
五、 fetch
a) 鐵律:雙向不要兩邊設置Eager(會有多餘的查詢語句發出)
b) 對多方設置fetch的時候要謹慎,結合具體應用,通常用Lazy不用eager,特殊狀況(多方數量很少的能夠考慮,提升效率的時候能夠考慮)
六、 O/RMapping編程模型
a) 映射模型
i. jpa annotation
ii. hibernate annotation extension
b) 編程接口
i. jpa
ii. hibernate
c) 數據查詢語言
i. HQL
ii. EJBQL(JPQL)
七、 要想刪除或者更新,先作load,除了精確知道ID以外
八、 若是想消除關聯關係,先設定關係爲null,再刪除對應記錄,若是不刪記錄,該記錄就變成垃圾數據
一、 Set
二、 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;}
三、 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也繼承了而且擴展了一個屬性
每棵類繼承樹使用一個表
把全部的屬性都要存儲表中,目前至少須要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表示鳥
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;}
}
<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>
一、理解如何映射
由於類繼承樹確定是對應多個類,要把多個類的信息存放在一張表中,必須有某種機制來區分哪些記錄是屬於哪一個類的。
這種機制就是,在表中添加一個字段,用這個字段的值來進行區分。用hibernate實現這種策略的時候,有以下步驟:
父類用普通的<class>標籤訂義
在父類中定義一個discriminator,即指定這個區分的字段的名稱和類型
如:<discriminator column=」XXX」 type=」string」/>
子類使用<subclass>標籤訂義,在定義subclass的時候,須要注意以下幾點:
Subclass標籤的name屬性是子類的全路徑名
在Subclass標籤中,用discriminator-value屬性來標明本子類的discriminator字段(用來區分不一樣類的字段)
的值Subclass標籤,既能夠被class標籤所包含(這種包含關係正是代表了類之間的繼承關係),也能夠與class標
籤平行。 當subclass標籤的定義與class標籤平行的時候,須要在subclass標籤中,添加extends屬性,裏面的值
是父類的全路徑名稱。子類的其它屬性,像普通類同樣,定義在subclass標籤的內部。
二、理解如何存儲
存儲的時候hibernate會自動將鑑別字段值插入到數據庫中,在加載數據的時候,hibernate能根據這個鑑別值
正確的加載對象
多態查詢:在hibernate加載數據的時候能鑑別出正真的類型(instanceOf)
get支持多態查詢
load只有在lazy=false,才支持多態查詢
hql支持多態查詢
父類中註解以下:
使用@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來註解標識字段的值
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))
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會根據單表繼承映射文件的配置內容,自動在插入數據時哪一個子類須要插入哪些字段,而且自動插入標識符字符值(在映射文件中配置了)
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();
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
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(也就不區分什麼子類,把全部子類所有加載上來。)
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();
在hibernate加載數據的時候能鑑別出正直的類型(經過instanceof)
get支持多態查詢;load只有在lazy=false,才支持多態查詢;HQL支持多態查詢
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();
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();
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");
}
}
* 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");
}
}
每一個類映射成一個表(table per subclass)
對象模型不用變化,存儲模型須要變化
每一個類映射成一個表(table per subclass)
注:由於須要每一個類都映射成一張表,因此Animal也映射成一張表(t_animal),表中字段爲實體類屬性
而pig子類也須要映射成一張表(t_pid),但爲了與父類聯繫須要加入一個外鍵(pidid)指向父類映射成的表(t_animal),字段爲子類的擴展屬性。Bird子類一樣也映射成一張表(t_bird),也加入一個外鍵(birdid)指向父類映射成的表(t_animal),字段爲子類的擴展屬性。
<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>
一、理解如何映射
這種策略是使用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標籤的內部。
由於,子類生成的表須要引用父類生成的表,因此只須要在父類設置具體表繼承映射就能夠了,其它子類只須要使用@Entity註解就能夠了
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Person {
private int id;
private String name;
@Id
@GeneratedValue
public int getId() {return id;}
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 concreteclass)(有一些限制)
對象模型不用變化,存儲模型須要變化
每一個具體類(Pig、Brid)映射成一個表(table per concrete class)(有一些限制)
t_pig
Id |
Name |
Sex |
Weight |
1 |
豬豬 |
True |
100 |
t_bird
Id |
Name |
Sex |
Height |
2 |
鳥鳥 |
False |
50 |
<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是不能重複的(不能使用自增生成主鍵)
只須要對父類進行註解就能夠了,
由於子類表的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;}
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是類型,只是存儲的位置不一樣而以)
注:若是不想t_animal存在(由於它沒有實際的做用),能夠設置<class>標籤中的abstract="true"(抽象表),這樣在導出至數據庫時,就不會生成t_animal表了。
<class name="com.wjt276.hibernate.Animal" table="t_animal" abstract="true">
<id name="id"column="id"><!-- 映射主鍵 -->
…………
一、 第一種:它把全部的數據都存入一個表中,優勢:效率好(操做的就是一個表);缺點:存在庸於字段,若是將庸於字段設置爲非空,則就沒法存入數據;
二、 第二種:井井有條,缺點:效率很差(表間存在關聯表)
三、 第三種:主鍵字段不能夠設置爲自增主鍵生成策略。
通常使用第一種
樹形結構:也就是目錄結構,有父目錄、子目錄、文件等信息,而在程序中樹形結構只是稱爲節點。
一棵樹有一個根節點,而根節點也有一個或多個子節點,而一個子節點有且僅有一個父節點(當前除根節點外),並且也存在一個或多個子節點。
也就是說樹形結構,重點就是節點,也就是咱們須要關心的節點對象。
節點:一個節點有一個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;}}
<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>
由於樹型節點全部的數據,在數據庫中只是存儲在一個表中,而對於實體類來講,節點對子節點來講是一對多的關係,而對於父節點來講是多對一的關係。所以能夠在一個實體類中註解。以下
@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);
}}}
一、 實體類(表)
二、 導航(編程方便)
a) 經過學生 取出 學生所先的課程
b) 可是經過課程 取出 學該課程的 學生很差。學的學生太多
c) 肯定編程的方式
三、 能夠利用聯合主鍵映射能夠,
a) 學生生成一個表
b) 課程生成一個表
c) 再生成一個表,主鍵是聯合主鍵(學生ID、課程ID) + 學生共生成一個表
四、 也能夠利用一對多,多對多 均可以(推薦)
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
HQL VS EJBQL
一、 NativeSQL:本地語言(數據庫本身的SQL語句)
二、 HQL :Hibernate自帶的查詢語句,可使用HQL語言,轉換成具體的方言
三、 EJBQL:JPQL 1.0,能夠認爲是HQL的一個子節(重點)
四、 QBC:Query By Cretira
五、 QBE:Query By Example
注意:上面的功能是從1至5的比較,1的功能最大,5的功能最小
一、 版塊
/** 版塊*/
@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;}}
二、 主題
/**主題*/
@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;}}
三、 主題回覆
/**主題回覆*/
@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;}}
四、 臨時類
/**臨時類 */
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
}
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());
}
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.asc(String propertyName)
升序排序
Order.desc(String propertyName)
降序排序
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)
返回某屬性值的合計
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();
複合查詢就是在原有的查詢基礎上再進行查詢。例如在顧客對定單的一對多關係中,在查詢出全部的顧客對象後,但願在查詢定單中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();
離線查詢又叫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();
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();
* 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,這樣不會出現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());
}
* 會再次發出查詢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());
}
一、 注意session.clear()的動用,尤爲在不斷分頁循環的時候
a) 在一個大集合中進行遍歷,遍歷msg,取出其中的含有敏感字樣的對象
b) 另一種形式的內存泄露 //面試是:Java有內存泄漏嗎?
二、 1 + N問題 //典型的面試題
a) Lazy
b) BatchSize 設置在實體類的前面
c) joinfetch
三、 list 和 iterate不一樣之處
a) list取全部
b) Iterate先取ID,等用到的時候再根據ID來取對象
c) session中list第二次發出,仍會到數據庫查詢
d) iterate第二次,首先找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
配置二級緩存的配置文件:模板文件位於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>
Hibernate中二級緩存默認就是開啓的,也能夠顯示的開啓
二級緩存是hibernate的配置文件設置以下:
<!--開啓二級緩存,hibernate默認的二級緩存就是開啓的 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
修改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.) |
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-write、nonstrict-read-write或 read-only。
(2) region (可選, 默認爲類或者集合的名字(class orcollection role name)) 指定第二級緩存的區域名(name of the secondlevel cache region)
(3) include (可選,默認爲 all) non-lazy 當屬性級延遲抓取打開時, 標記爲lazy="true"的實體的屬性可能沒法被緩存
另外(首選?), 你能夠在hibernate.cfg.xml中指定<class-cache>和 <collection-cache> 元素。
這裏的usage 屬性指明瞭緩存併發策略(cache concurrency strategy)。
若是你的應用程序只需讀取一個持久化類的實例,而無需對其修改, 那麼就能夠對其進行只讀 緩存。這是最簡單,也是實用性最好的方法。甚至在集羣中,它也能完美地運做。
<class name="eg.Immutable" mutable="false">
<cache usage="read-only"/>
....
</class>
若是應用程序須要更新數據,那麼使用讀/寫緩存 比較合適。 若是應用程序要求「序列化事務」的隔離級別(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()調用前, 整個事務已經結束。
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) |
沒有變化,近似於靜態的數據。
一、 清除指定實體類的全部數據
SessionFactory.evict(Student.class);
二、 清除指定實體類的指定對象
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);
}
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
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不使用
一、 LRU(最近最少使用)、LFU(最近最少訪問)、FIFO(先進先出)
一、 ReadUncommited(未提交讀):沒有提交就能夠讀取到數據(發出了Insert,但沒有commit就能夠讀取到。)不多用
二、 ReadCommited(提交讀):只有提交後才能夠讀,經常使用,
三、 RepeatableRead(可重複讀):mysql默認級別, 必需提交才能見到,讀取數據時數據被鎖住。
四、 Serialiazble(序列化讀):最高隔離級別,串型的,你操做完了,我才能夠操做,併發性特別很差,
隔離級別 |
是否存在髒讀 |
是否存在不可重複讀 |
是否存在幻讀 |
Read Uncommitted(未提交讀) |
Y |
Y |
Y |
Read Commited(提交讀) |
N |
Y(可採用悲觀鎖解決) |
Y |
Repeatable Read(可重複讀) |
N |
N |
Y |
Serialiazble(序列化讀) |
|
|
|
髒讀:沒有提交就能夠讀取到數據稱爲髒讀
不可重複讀:再重複讀一次,數據與你上的不同。稱不可重複讀。
幻讀:在查詢某一條件的數據,開始查詢的後,別人又加入或刪除些數據,再讀取時與原來的數據不同了。
一、 Mysql查看數據庫隔離級別:
方法:select@@tx_isolation;
二、 Mysql數據庫修改隔離級別:
方法:set transactionisolation level 隔離級別名稱;
例如:修改成未提交讀:settransaction isolation level read uncommitted;
ACID即:事務的原子性、一致性、獨立性及持久性
事務的原子性:是指一個事務要麼所有執行,要麼不執行.也就是說一個事務不可能只執行了一半就中止了.好比你從取款機取錢,這個事務能夠分紅兩個步驟:1劃卡,2出錢.不可能劃了卡,而錢卻沒出來.這兩步必須同時完成.要麼就不完成.
事務的一致性:是指事務的運行並不改變數據庫中數據的一致性.例如,完整性約束了a+b=10,一個事務改變了a,那麼b也應該隨之改變.
事務的獨立性:是指兩個以上的事務不會出現交錯執行的狀態.由於這樣可能會致使數據不一致.
事務的持久性:是指事務運行成功之後,就係統的更新是永久的.不會平白無故的回滾.
一、第一類丟失更新(Lost Update)
時間 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hibernate談到悲觀鎖、樂觀鎖,就要談到數據庫的併發問題,數據庫的隔離級別越高它的併發性就越差
併發性:當前系統進行了序列化後,當前讀取數據後,別人查詢不了,看不了。稱爲併發性很差
數據庫隔離級別:見前面章級
悲觀鎖:具備排他性(我鎖住當前數據後,別人看到不此數據)
悲觀鎖通常由數據機制來作到的。
一般依賴於數據庫機制,在整修過程當中將數據鎖定,其它任何用戶都不能讀取或修改(如:必需我修改完以後,別人才能夠修改)
悲觀鎖通常適合短事務比較多(如某一數據取出後加1,當即釋放)
長事務佔有時間(若是佔有1個小時,那麼這個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>
若是須要使用悲觀鎖,確定在加載數據時就要鎖住,一般採用數據庫的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();
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來維護的。
樂觀鎖在存儲數據時不用關心
<div id="digg" articleid="20119763"> <dl id="btnDigg" class="digg digg_disable" onclick="btndigga();"> <dt>頂</dt> <dd>1</dd> </dl> <dl id="btnBury" class="digg digg_disable" onclick="btnburya();"> <dt>踩</dt> <dd>0</dd> </dl> </div> <div class="tracking-ad" data-mod="popu_222"><a href="javascript:void(0);" target="_blank"> </a> </div> <div class="tracking-ad" data-mod="popu_223"> <a href="javascript:void(0);" target="_blank"> </a></div> <script type="text/javascript"> function btndigga() { $(".tracking-ad[data-mod='popu_222'] a").click(); } function btnburya() { $(".tracking-ad[data-mod='popu_223'] a").click(); } </script>
<div style="clear:both; height:10px;"></div> <div class="similar_article"> <h4></h4> <div class="similar_c" style="margin:20px 0px 0px 0px"> <div class="similar_c_t"> 相關文章推薦 </div> <div class="similar_wrap tracking-ad" data-mod="popu_36" style="max-height:195px;"> <ul class="similar_list fl"> <li> <em>•</em> <a href="http://blog.csdn.net/z2007130205/article/details/7982892" title="hibernate 馬士兵學習筆記" strategy="" target="_blank" style="width: 290px;">hibernate 馬士兵學習筆記</a> </li> <li> <em>•</em> <a href="http://huibuqule.iteye.com/blog/1882956" title="第五:馬士兵Struts2 視頻學習筆記之用通配符作配置將配置量降到最低" strategy="" target="_blank" style="width: 290px;">第五:馬士兵Struts2 視頻學習筆記之用通配符作配置將配置量降到最低</a> </li> <li> <em>•</em> <a href="http://blog.csdn.net/kexinmei/article/details/7292309" title="hibernate 學習筆記(馬士兵教程) 次日" strategy="" target="_blank" style="width: 290px;">hibernate 學習筆記(馬士兵教程) 次日</a> </li> <li> <em>•</em> <a href="http://jtlyuan.iteye.com/blog/1316902" title="馬士兵Hibernate學習筆記" strategy="" target="_blank" style="width: 290px;">馬士兵Hibernate學習筆記</a> </li> <li> <em>•</em> <a href="http://huibuqule.iteye.com/blog/1883436" title="第六:馬士兵Struts2 視頻學習筆記之參數傳遞" strategy="" target="_blank" style="width: 290px;">第六:馬士兵Struts2 視頻學習筆記之參數傳遞</a> </li> </ul> <ul class="similar_list fr"> <li> <em>•</em> <a href="http://sheer.iteye.com/blog/1887914" title="Java複習總結_馬士兵Java教學視頻學習筆記(一)__遞歸" strategy="" target="_blank" style="width: 290px;">Java複習總結_馬士兵Java教學視頻學習筆記(一)__遞歸</a> </li> <li> <em>•</em> <a href="http://blog.csdn.net/moluls135/article/details/43674751" title="[學習筆記]馬士兵 Servlet & JSP(1.Servlet)" strategy="" target="_blank" style="width: 290px;">[學習筆記]馬士兵 Servlet & JSP(1.Servlet)</a> </li> <li> <em>•</em> <a href="http://huibuqule.iteye.com/blog/1882939" title="第四:馬士兵Struts2 視頻學習筆記之action類DMI" strategy="" target="_blank" style="width: 290px;">第四:馬士兵Struts2 視頻學習筆記之action類DMI</a> </li> <li> <em>•</em> <a href="http://blog.csdn.net/qq435967718/article/details/7292220" title="Hibernate -annotation 學習筆記 1 馬士兵" strategy="" target="_blank" style="width: 290px;">Hibernate -annotation 學習筆記 1 馬士兵</a> </li> <li> <em>•</em> <a href="http://blog.csdn.net/zhtzyh2012/article/details/46447229" title="2015061003 - 馬士兵hibernate學習筆記" strategy="" target="_blank" style="width: 290px;">2015061003 - 馬士兵hibernate學習筆記</a> </li> </ul> </div> </div> </div>