Hibernate學習筆記

Hibernate學習筆記java


Java相關課程系列筆記之十四程序員


筆記內容說明算法


Hibernate(梁建全老師主講,佔筆記內容100%);sql

 

目 錄
1、 Hibernate的概述...................................................................................................................1
1.1Hibernate框架的做用........................................................................................................1
1.2 Hibernate訪問數據庫的優勢............................................................................................ 1
1.3 JDBC訪問數據庫的缺點.................................................................................................. 1
1.4 Hibernate的設計思想........................................................................................................ 1
2、 Hibernate的基本使用...........................................................................................................2
2.1 Hibernate的主要結構........................................................................................................2
2.2Hibernate主要的API........................................................................................................2
2.3 Hibernate使用步驟............................................................................................................2
2.4HQL語句(簡要介紹)...................................................................................................6 數據庫

3、 數據映射類型.......................................................................................................................7
3.1映射類型的做用.................................................................................................................7
3.2 type映射類型的兩種寫法.................................................................................................7數組

4、 Hibernate主鍵生成方式.......................................................................................................8
4.1五種生成方式.....................................................................................................................8瀏覽器

5、 Hibernate基本特性...............................................................................................................9
5.1對象持久性.........................................................................................................................9
5.2處於持久狀態的對象具備的特色.....................................................................................9
5.3三種狀態下的對象的轉換.................................................................................................9
5.4批量操做:注意及時清除緩存.........................................................................................9
5.5案例: 三種狀態下的對象使用....................................................................................... 10
5.6一級緩存機制(默認開啓)...........................................................................................10
5.7一級緩存的好處...............................................................................................................10
5.8管理一級緩存的方法....................................................................................................... 10
5.9延遲加載機制...................................................................................................................11
5.10具備延遲加載機制的操做............................................................................................. 11
5.11常犯的錯誤.....................................................................................................................12
5.12延遲加載的原理.............................................................................................................12
5.13 Session的 get和 load方法的區別................................................................................ 12
5.14延遲加載的好處.............................................................................................................12
5.15案例: 測試延遲加載..................................................................................................... 12
5.16案例: 重構 NetCTOSS資費管理模塊.........................................................................13
5.17 Java Web程序中如何用延遲加載操做 (OpenSessionInView) ................................ 15緩存

6、 關聯映射............................................................................................................................. 18
6.1一對多關係 one-to-many................................................................................................. 18
6.2多對一關係 many-to-one................................................................................................. 19
6.3多對多關聯映射 many-to-many...................................................................................... 19
6.4關聯操做 (查詢 join fetch/級聯 cascade) ....................................................................21
6.5繼承關係映射...................................................................................................................24服務器

7、 Hibernate查詢方法.............................................................................................................27
7.1HQL查詢.........................................................................................................................27
7.2 HQL和 SQL的相同點...................................................................................................27session

7.3 HQL和 SQL的不一樣點....................................................................................................27
7.4HQL典型案例.................................................................................................................27
7.5 Criteria查詢.....................................................................................................................30
7.6Native SQL原生 SQL查詢.............................................................................................31

8、 Hibernate高級特性.............................................................................................................32
8.1二級緩存...........................................................................................................................32
8.2二級緩存開啓方法及測試............................................................................................... 32
8.3二級緩存管理方法...........................................................................................................33
8.4二級緩存的使用環境.......................................................................................................33
8.5查詢緩存...........................................................................................................................33
8.6查詢緩存開啓方法及測試............................................................................................... 33
8.7查詢緩存的使用環境.......................................................................................................33

9、 Hibernate鎖機制.................................................................................................................34
9.1悲觀鎖...............................................................................................................................34
9.2悲觀鎖的實現原理...........................................................................................................34
9.3悲觀鎖使用步驟及測試...................................................................................................34
9.4樂觀鎖...............................................................................................................................35
9.5樂觀鎖的實現原理...........................................................................................................35
9.6樂觀鎖使用步驟及測試...................................................................................................35

10、 其餘注意事項.....................................................................................................................36
10.1源碼服務器管理工具.....................................................................................................36
10.2利用 MyEclipse根據數據表自動生成實體類、 hbm.xml...........................................36
10.3根據實體類和 hbm.xml生成數據表.............................................................................37
10.4 Hibernate中分頁查詢使用 join fatch的缺點...............................................................37
10.5 Hibernate的子查詢映射................................................................................................ 38

 Hibernate 的概述一

、.1 Hibernate框架的做用

Hibernate框架是一個數據訪問框架(也叫持久層框架,可將實體對象變成持久對象,詳見第5章)。經過 Hibernate框架能夠對數據庫進行增刪改查操做, 爲業務層構建一個持久層。可使用它替代之前的 JDBC訪問數據。
1.2 Hibernate訪問數據庫的優勢
1)簡單,能夠簡化數據庫操做代碼。
2) Hibernate能夠自動生成 SQL,能夠將 ResultSet中的記錄和實體類自動的映射(轉化)。
Hibernate不和數據庫關聯,是一種通用的數據庫框架(支持30多種數據庫),
方便數據庫移植。任何數據庫均可以執行它的 API。由於 Hibernate的 API中是不涉及 SQL
語句的, 它會根據 Hibernate的配置文件, 自動生成相應數據庫的 SQL語句。
1.3 JDBC訪問數據庫的缺點

須要編寫大量的複雜的 SQL語句、 表字段多時 SQL也繁瑣、 設置各個問號值。
須要編寫實體對象和記錄之間的代碼, 較爲繁瑣。數據庫移植時須要修改大量的 SQL語句。
1.4 Hibernate的設計思想
Hibernate是基於ORM(Object Relation Mapping)思想設計的,稱爲對象關係映射。負

責 Java
Hibernate是一款主流的 ORM工具,還有其餘不少 ORM工具,如: MyBatis(之前叫 iBatis)、
JPA。 Hibernate功能比 MyBatis強大些, 屬於全自動類型, MyBatis屬於半自動。但全自動會

有些不可控因素, 所以有些公司會用 MyBatis。
ORM工具在完成 Java對象和數據庫之間的映射後:
在查詢時, 直接利用工具取出「對象」(不管是查詢一條記錄仍是多條記錄, 取出的
都是一個個對象, 咱們不用再去轉化實體了)。
在增刪改操做時,直接利用工具將「對象」更新到數據庫表中(咱們不用再去把對象
轉成數據了)。
中間的 SQL+JDBC細節, 都被封裝在了工具底層, 不須要程序員參與。注意事項:
v Java程序想訪問數據庫, 只能經過 JDBC的方式,而 Hibernate框架也就是基於
ORM思想對 JDBC的封裝。
v Hibernate是以「對象」爲單位進行數據庫的操做。

 

 

 

 

 


2、 Hibernate的基本使用


2.1 Hibernate的主要結構
1) hibernate.cfg.xml(僅1個): Hibernate的主配置文件,主要定義數據鏈接參數和框架
設置參數。
u 注意事項:就是個xml文件,只是名字比較奇葩!
2) Entity實體類(n個,一個表一個):主要用於封裝數據庫數據。
3) hbm.xml映射文件(n個): 主要描述實體類和數據表之間的映射信息。 描述表與類,字段與屬性的對應關係。
u 注意事項: hbm.xml是個後綴,如:命名可寫Cost.hbm.xml。
2.2 Hibernate主要的 API
1) Configuration: 用於加載 hibernate.cfg.xml配置信息。 用於建立 SessionFactory。
2) SessionFactory:存儲了 hbm.xml中描述的信息,內置了一些預編譯的 SQL,能夠建立 Session對象。
3) Session: 負責對數據表執行增刪改查操做。 表示 Java程序與數據庫的一次鏈接會話,是對之前的 Connection對象的封裝。 和 JSP中的 session不是一回事, 就是名字同樣而已。
4) Query:負責對數據表執行特殊查詢操做。
5) Transaction: 負責 Hibernate操做的事務管理。 默認狀況下 Hibernate事務關閉了自動
提交功能, 須要顯式的追加事務管理(如調用 Transaction對象中的 commit();提交事務)!
u 注意事項:
v 這些 API都是在 Hibernate包下的, 導包別導錯!
v 第一次訪問數據庫比較慢,比較耗資源,由於加載的信息多。
2.3 Hibernate使用步驟
step1:創建數據庫表。
step2:創建 Java工程(Web工程也可),引入Hibernate開發包和數據庫驅動包。必須引入的包: hibernate3.jar、 cglib.jar、 dom4j.jar、 commons-collections.jar、 commons-logging.jar„等
step3: 添加 hibernate.cfg.xml配置文件, 文件內容以下:
<?xml version='1 .0' encoding='UTF-8'?>
<! DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD3.0//EN"
"http://hibernate. sourceforge.net/hibernate-configuration-3 .0.dtd">
<hibernate-configuration>
<session-factory>
<property name="dialect"><!-- 指定方言, 決定 Hibernate生成哪一種 SQL-->
org.hibernate.dialect.OracleDialect<!-- 不知道數據庫版本就寫 OracleDialect-->
</property><!--可在 hibernate3.jar中 org.hibernate.dialect包下查看名字-->
<property name="connection.url">
jdbc:oracle:thin: @localhost: 1 52 1 :dbchang
</property>
<property name="connection.username">system</property>
<property name="connection.password">chang</property>

 

 

 

 

 


<property name="connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
<!--框架參數, 將 hibernate底層執行的 SQL語句從控制檯顯示-->
<property name="show_sql">true</property>
<!--格式化顯示的 SQL-->
<property name="format_sql">true</property>
<!--指定映射描述文件-->
<mapping resource="org/tarena/entity/Cost.hbm.xml" />
</session-factory>
</hibernate-configuration>


u 注意事項:應該放在源文件的 src目錄下,默認爲 hibernate.cfg.xml。文件內容是
Hibernate工做時必須用到的基礎信息。
step4:編寫Entity實體類(也叫 POJO類),例如:資費實體類Cost
private Integer id; //資費 ID private String feeName; //資費名稱
private Integer baseDuration; //基本時長 private Float baseCost; //基本定費
private Float unitCost; //單位費用 private String status; //0: 開通; 1: 暫停;private String descr; //資費信息說明 private Date createTime; //建立日期
private Date startTime; //啓用日期 private String costType; //資費類型
„„getter/setter方法
u 注意事項: POJO類表示普通類(Plain Ordinary Old Object),沒有格式的類,只有
屬性和對應的 getter/setter方法,而沒有任何業務邏輯方法的類。這種類最多再加入
equals()、 hashCode()、 toString()等重寫父類 Object的方法。不承擔任何實現業務邏

輯的責任。
step5:編寫hbm.xml映射(文件)描述信息:映射文件用於指明 POJO類和表之間的映射關係(xx屬性對應xx字段),一個類對應一個映射文件。例如: Cost.hbm.xml內容以下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 定義 COST_CHANG表和 Cost類型之間的映射信息 -->
<hibernate-mapping><!-- <hibernate-mapping package="包名寫這也行"> -->
<!--name:包名.類名,指定是哪一個類; table:數據庫中哪一個表; catalog:對Oracle而言爲哪一個數據庫, 對 MySQl而言爲某個用戶(MySQl是在用戶下建表, Oracle是在庫中建表),不寫也行(若用工具則會自動生成)。例如, select* from cost_chang則會在 hibernate.cfg配置文件中定義的庫(或用戶)下去找表。若寫了則爲 select*from system.cost_chang -->
<class name="org.tarena.entity.Cost" table="COST_CHANG" catalog="system">
<!-- <id></id>代表此爲主鍵列,且必須寫不然xml報錯,主鍵映射-->
<id name="id" type="java.lang.Integer">
<column name="ID" /><!-- 或雙標籤<column name="ID"></column> -->
<!--指定主鍵值生成方式,採用序列方式生成主鍵,僅對添加操做有效-->
<generator class="sequence">
<param name="sequence">COST_SEQ_CHANG</param> <!--指定序列名-->
</generator>
</id>
<propertyname="name"type="java.lang.String"><!--如下爲非主鍵映射-->
<column name="NAME" /><!--可有 length、 not-null屬性, 如: length="20" -->
</property>
<propertyname="baseDuration"type="java.lang.Integer"><!--映射順序不要緊-->
<column name="BASE_DURATION" />
</property>
<property name="baseCost" type="java.lang.Float"><!--類型要和實體定義的相同 -->
<column name="BASE_COST" />
</property>
<property name="startTime" type="java.sql.Date"><!--列名寫錯則報錯讀不到實體-->
<column name="STARTIME" /><!--junit測試右鍵點 Copy Trace查看錯誤列-->
</property>
<!--也可寫成<property name=" " type=" " column=" "></property>, 主鍵列同理!-->
„„„„其餘省略„„„„
</class>
</hibernate-mapping>
u 注意事項:
v 映射文件默認與POJO類放在一塊兒;命名規則爲:類名.hbm.xml。
v hbm.xml中已寫出的屬性與字段的映射要一一對應,若表中沒有某個字段,卻
寫了映射關係,則報錯:找不到實體類。
step6: 利用 HibernateAPI實現 DAO
1)新建 HibernateUtil類,用於封裝建立 Session的方法。以下:每一個用戶會對應一個
Session, 可是 SessionFactory是共享的。

 

public class HibernateUtil{
private static SessionFactory sf;
static{//不用每次都加載配置信息, 因此放 static塊中, 不然每次都加載會耗費資源
Configuration conf=new Configuration();//加載主配置 hibernate.cfg.xml
conf.configure("/hibernate.cfg.xml");
sf=conf.buildSessionFactory();//獲取SessionFactory }
public static Session getSession(){//獲取 Session
Sessionsession=sf.openSession(); returnsession; } }
2) 新建 CostDAO接口
public Cost findById(int id); public void save(Cost cost);
public void delete(int id); public void update(Cost cost);
public List<Cost> findAll();
3)新建 CostDAOImpl類, 用於實現 CostDAO接口
public class CostDAOImpl implements CostDAO{
private Session session;
public CostDAOImpl() {//不想老寫得到 session的方法, 就寫在構造器中
session=HibernateUtil.getSession(); }
/** get方法執行查詢, 按主鍵當條件查詢, 如何判斷是主鍵, 是根據寫的描述文件來定,get方法就是findById,就是按主鍵去查,需指定:操做哪一個類和id(主鍵)條件值便可,其餘條件查詢作不了*/
public Cost findById(int id) {
//Session session=HibernateUtil.getSession();
Costcost=(Cost)session.get(Cost.class,id); session.close(); returncost; }
/**save方法執行增長操做,注意1:獲取事務並開啓,增刪改要注意,查詢能夠無論事務,由於沒對數據庫進行修改;注意2:主鍵值根據 hbm.xml中的<generator>定義生成,執行後, 會先獲取序列值, 再去作 insert操做。
即先: selectCOST_SEQ_CHANG.nextvalfromdual;而後: insertinto„„ */
public void save(Cost cost) {
//Session session=HibernateUtil.getSession();
Transactiontx=session.beginTransaction();//打開事務 session.save(cost);
tx.commit();//提交事務 session.close();//釋放 }
/** delete方法執行刪除操做,因爲 Hibernate以「對象」爲單位進行數據庫操做,因此這裏要傳進去一個對象,雖然是個對象,但仍是按主鍵作條件刪除,只要把主鍵值設置上就行,其餘非主鍵值不用管。也可先經過id查再刪*/
public void delete(int id) {
//Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction(); Cost cost=new Cost();
cost.setId(id); session.delete(cost); tx.commit(); session.close(); }
/** update方法執行修改操做, */
public void update(Cost cost) {
//Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
session.update(cost);//將 cost對象更新到數據庫 tx.commit();
session.close();
}

 

/**特殊查詢, SQL語句: String sql="select* from COST_CHANG";
HQL語句: String hql="from Cost"; (Hibernate Query Language)是面向對象的查詢語句。from後寫映射的類名,它是Hibernate中特有的查詢語句,根據映射的類去查詢。 */
public List<Cost> findAll() {
//Session session=HibernateUtil.getSession();
String hql="from Cost";//HQL語句
Query query=session.createQuery(hql);
List<Cost> list=query.list();//執行查詢, 返回 List集合
session.close(); returnlist; } }
4) 新建 TestCostDAO類, 使用 junit測試
@Test
public void testFindById(){//當 get方法沒有記錄時, 返回 null
CostDAO costDao= new CostDAOImpl(); Cost cost= costDao.findById(1);
System.out.println(cost.getName());System.out.println(cost.getBaseDuration());
System.out.println(cost.getBaseCost()); System.out.println(cost.getUnitCost());
System.out.println(cost.getDescr()); }
@Test
public void testSave(){//id主鍵列由 Hibernate管理, 這裏不用設置
Cost cost=newCost(); cost.setName("2013計時");
cost.setUnitCost(0.8f); cost.setDescr("2013-08-09計時, 0.8元/小時。");
cost.setStatus("0"); cost.setCreaTime(newDate(System.currentTimeMillis()));
CostDAO costDao= new CostDAOImpl(); costDao.save(cost); }
@Test
public void testUpdate(){//開通某個資費, 把狀態由 0變爲1
CostDAO costDAO=new CostDAOImpl();
/**注意事項:更新部分字段,不能和實現類中的刪除那樣,作一個對象出來!否
則沒設置的字段將被改成空! 即不能: Costcost=newCost(); cost.setId(90);
cost. setStatus(" 1 "); cost.setStartTime(new Date(System.currentTimeMillis())); */
Cost cost=costDAO.findById(90);//只能先經過 id找到帶有全部值的對象
cost.setStatus(" 1 ");//而後再對部分字段進行更新, 才能避免把其餘字段更新爲空
cost.setStartTime(new Date(System.currentTimeMillis()));
costDAO.update(cost); }
@Test
public void testDelete(){
CostDAOcostDAO=newCostDAOImpl(); costDAO.delete(90); }
@Test
public void testFindAll(){
CostDAO costDAO=new CostDAOImpl(); List<Cost> list=costDAO.findAll();
for(Costc:list){ System.out.println(c.getName()); } }
2.4 HQL語句 (簡要介紹)
簡要介紹見2.3節中 step6中的3)特殊查詢(本頁最上)。詳細介紹見第七章。

 

 

 

3、 數據映射類型

 

hbm.xml在描述字段和屬性映射時, 採用 type屬性來指定映射類型。
3 .1 映射類型的做用
主要負責實現屬性和字段值之間的相互轉化。
3.2 type 映射類型的兩種寫法
1)指定 Java類型,例如: java.lang.String、 java.lang.Integer„,不能寫成String„ 2)指定Hibernate類型,例如:
①整數: byte、 short、 integer、 long;
②浮點數: float、 double; ③字符串: string;
④日期和時間: date(只處理年月日), time(只處理時分秒), timestamp(處理年
月日時分秒);
⑤布爾值: true/false<-yes_no->char(1)(數據庫存 Y/N, 顯示時自動轉爲 true/false)、
true/false<-true_false->char(1) (數據庫存 T/F, 顯示時自動轉爲 true/false)、
true/false<-boolean->bit (數據庫存1/0, 顯示時自動轉爲 true/false);
⑥其餘: blob(以字節爲單位存儲大數據)、 clob(以字符爲單位存儲大數據)、
big_decimal、 big_integer;
u 注意事項: Hibernate類型都是小寫!建議使用 Hibernate類型。
3)因此2.3節中 step5的 Cost.hbm.xml內的 type也可這樣寫:
<property name="name" type="string"><column name="NAME" /></property>
<property name="baseCost" type="float"><column name="BASE_COST" /></property>
<property name="startTime" type="date"><column name="STARTIME" /></property>
u 注意事項:
v java.util.Date有年月日時分秒毫秒,但若是用 date映射,則只把年月日存進數
據庫; java.sql.Date只有年月日。 java.sql.Timestamp有年月日時分秒毫秒。
v 若在頁面顯示按特定格式顯示則用 Struts2標籤:
<s: date name="屬性名" format="yyyy-MM-dd HH: mm: ss"/>

4、 Hibernate主鍵生成方式
Hibernate 負責管理主鍵值。 它提供了多種主鍵生成方式。
4.1五種生成方式
1) sequence:能夠按指定序列生成主鍵值。只適用於 Oracle數據庫。不擔憂併發量!
例如: <generator class="sequence">
<param name="sequence">序列名字</param></generator>
u 注意事項:建立序列時若是不指定參數,默認從1開始,步進是1。
2) identity:按數據庫自動增加機制生成主鍵值。通常適用於 MySql、 SQLServer數據庫。
例如: <generator class="identity"></generator>
3) native: Hibernate 會根據方言類型不一樣, 選擇不一樣的主鍵生成方式。 若是是OracleDialect則會選擇 sequence, 若是是 MySQLDialect則會選擇 identity。
例如: <generator class="native"></generator>
u 注意事項:若是是 MySql數據庫,<paramname="sequence">序列名字</param>是不
起做用的, 但也不會出錯; 若是是 Oracle數據庫, <param name="sequence">序列名
字</param>就會起做用, 因此通常咱們會加上這句話, 這樣通用性更強。
4) assigned: Hibernate會放棄主鍵生成,採用此方法,須要在程序中指定主鍵值。
例如: <generator class="assigned"></generator>
5) increment: Hibernate先執行 selectmax(id)...語句獲取當前主鍵的最大值,執行加1操做, 而後再調用 insert語句插入。 Oracle和 MySQL均可用。 但不適合併發量很大的狀況!
例如: <generator class="increment"></generator>
6) uuid/hilo: uuid:按UUID算法生成一個主鍵值(字符串類型); hilo:按高低位算法生成一個主鍵值 (數值類型)。
例如: <generator class="hilo"></generator>
u 注意事項:
v 主鍵通常都是自動生成的。咱們通常不使用業務數據做爲主鍵,由於業務邏輯
的改變有可能會改變主鍵值。
v 主鍵生成方式是枚舉類型,只能從一個有限的範圍內選擇,不能自定義。其中,
sequence是使用序列生成主鍵(Oracle數據庫常用)。

 

 

 

 

 

 

 

5.1對象持久性


5、 Hibernate基本特性

 


在 Hibernate使用過程當中, 實體對象能夠具備如下三種狀態:
1)臨時狀態:採用 new關鍵字建立的對象,該對象未與 Session發生關聯(未調用
Session的 API)。 也叫臨時對象。 臨時狀態的對象會被 Java的垃圾回收機制回收。
2)持久狀態:實體對象與 Session發生關聯(調用了 Session的get、 load、 save、 update 等 API)。 也叫持久對象。
3)遊離狀態:原來是持久狀態,後來脫離了 Session的管理。如: Session被關閉,對象
將從持久狀態變爲遊離狀態, 同時垃圾回收機制能夠回收掉, 再也不佔用緩存空間了。
5.2處於持久狀態的對象具備的特色
對象生命期持久,垃圾回收機制不能回收。
對象的數據能夠與數據庫同步 (即對象中的數據發生改變, 則數據庫中的數據自動同
步)。 由 Session對象負責管理和同步。
對象在 Session的一級緩存中存放 (或者說在 Session緩存中的對象都是持久對象)。
注意事項: Session.close();有兩個做用:①關閉鏈接、釋放資源②使對象變爲遊離狀
態,當對象的引用不存在時,對象才被回收。沒被回收時,對象中的數據還在!
5.3 三種狀態下的對象的轉換

 

 

 

 5.4批量操做: 注意及時清除緩存

Transaction tx= session.beginTransaction();
for(int i=0;i<100000;i++){ Foo foo= newFoo();
session.save(foo);//設置 foo屬性
if(i%50==0){//夠50個對象, 與數據庫同步下, 並清除緩存
session.flush();//同步
session.clear();//清除緩存
} }
tx.commit();

5.5案例: 三種狀態下的對象使用
當持久對象數據改變後, 調用 session.flush()方法,會與數據庫同步更新。
commit()方法, 內部也調用了 flush()方法, 所以使用 commit()方法能夠省略 flush()方法的調用。
@Test
publicvoidtest1(){//Foo實體有id、 name、 salary、 hireDate、 marry等屬性
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
Foo foo=(Foo)session.get(Foo.class, 1);//foo具備持久性
foo.setName("chang"); foo.setSalary(6000);
/**提交事務,若後面不寫 flush,只寫提交 commit,則也能執行更新操做。由於 commit 在內部會先調用 flush,再提交事務,因此此時flush可不寫*/
tx.commit();
/**觸發同步動做,同步和提交是兩回事。數據有變化才同步(更新),沒變化不會更新*/
session.flush(); session.close();//關閉session釋放資源 }
@Test
public void test2(){
Foofoo=newFoo(); foo.setName("tigger"); foo.setSalary(8000);
foo.setMarry(true); foo.setHireDate(newDate(System.currentTimeMillis()));
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
session.save(foo);//以上都是臨時狀態, 此時由臨時狀態轉爲持久狀態
foo.setSalary(10000);//修改 foo持久對象的數據, 也是更新操做, 可不寫 update方法
tx.commit();//同步、提交 session.close(); }
@Test
public void test3(){
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
Foo foo=(Foo)session.get(Foo.class, 2);//foo具備持久性
session.clear();//下面的則不會與數據庫同步了
foo.setName("常"); foo.setSalary(8800);
tx.commit(); //session.flush();//數據有變化才同步 update,沒變化不會update session.close(); }
5.6一級緩存機制 (默認開啓)
一級緩存被稱爲 Session級別的緩存: 每一個 Session都有本身獨立的緩存區, 該緩存區隨
着 Session建立而開闢(由 SessionFactory建立), 隨着 Session.close()而釋放。
該緩存區能夠存儲當前 Session關聯的持久對象。 只有在緩存區中, Session才管該對象。
5.7一級緩存的好處
Hibernate在查詢時,先去緩存當中查找,若是緩存中沒有,纔去數據庫查詢。若是利用Session對同一個對象查詢屢次,第一次去數據庫查,後續的會從緩存查詢,從而減小了與數據庫的交互次數。
5.8 管理一級緩存的方法
1) session.evict()方法:將對象清除。

2) session.clear()方法:清除全部對象。
3) session.close()方法:清除全部對象,並關閉與數據庫的鏈接。
u 注意事項: 不一樣的 session的緩存區不能交叉訪問。
4)案例:測試一級緩存
@Test
public void test1(){ Session session=HibernateUtil.getSession();
Foo foo1=(Foo)session.get(Foo.class, 1);//第一次查詢
System.out.println(foo1 .getName());//能出現 SQL 查詢語句
Foo foo2=(Foo)session.get(Foo.class, 1);//後續查詢
System.out.println(foo2.getSalary());//不能出現 SQL查詢語句
session.close(); } @Test
public void test2(){ Session session=HibernateUtil.getSession();
Foo foo1=(Foo)session.get(Foo.class, 1);//第一次查詢
System.out.println(foo1 .getName());//能出現 SQL 查詢語句
session.evict(foo1); //或session.clear(foo1);
Foo foo2=(Foo)session.get(Foo.class, 1);//後續查詢
System.out.println(foo2.getSalary());//能出現 SQL查詢語句
session.close(); } @Test
public void test3(){ Session session=HibernateUtil.getSession();
Foo foo1=(Foo)session.get(Foo.class, 1);//不一樣的對象, 因此查詢兩次
System.out. println(foo1 .getName());
Foofoo2=(Foo)session.get(Foo.class,2); System.out.println(foo2.getName()); } @Test
public void test4(){ Session session=HibernateUtil.getSession();
Foo foo1=(Foo)session.get(Foo.class, 1); System.out.println(foo1.getName());
session.close();
session=HibernateUtil.getSession();//不一樣的 session 的緩存區不能交叉訪問
Foo foo2=(Foo)session.get(Foo.class, 1);//又一次查詢
System.out.println(foo2.getName()); }
5.9延遲加載機制
Hibernate在使用時, 有些 API操做是具備延遲加載機制的。
延遲加載機制的特色:當經過 Hibernate的 API獲取一個對象結果後,該對象並無數據庫數據。 當經過對象的 getter方法獲取屬性值時, 纔去數據庫查詢加載。
5.10 具備延遲加載機制的操做
1) session.load();//延遲加載查詢, session.get();是當即加載查詢
2) query.iterator();//查詢
3)獲取關聯對象的屬性信息
u 注意事項:這些方法返回的對象,只有id屬性(主鍵)有值,其餘屬性數據在使用
的時候(調用 getXXX()方法時,除主鍵外,調主鍵 get方法不會發 SQL)纔去獲取。


5.11常犯的錯誤

1)報錯: LazyInitializationException: could not initialize proxy- no Session, 緣由: 代碼
中使用了延遲加載操做, 可是 session在加載數據前關閉了。 只要看到這個類名:
LazyInitializationException就都是 session過早關閉, 後面的描述可能不一樣。
2)報錯: NonUniqueObjectException: a different object with the same identifier value was already associated with the session, 緣由: 有兩個不一樣對象, 可是主鍵, 即 id卻相同。例如:
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
Account account1=(Account)session.get(Account.class, 1010);//將 account1放入緩存
System.out.println(account1 .getRealName());System.out.println(account1 .getIdcardNo());
Account account2=newAccount(); account2.setId(1010);
//update操做會將 accont2放入緩存,此時會出現異常,緩存中已經存在一個1010對象,
不容許再放入 id爲1010的對象
session.update(account2); tx.commit(); session.close();
5.12延遲加載的原理
在使用延遲加載操做後, Hibernate返回的對象是 Hibernate利用 CGLIB技術 (cglib.jar)新生成的一個類型 (動態的在內存中生成)。 在新類型中, 將屬性的 getter方法重寫。 新生成的類是原實體類的子類 (繼承關係)。
例如: public class Foo$$EnhancerByCGLIB$$87e5f322 extends Foo{
public String getName(){
//判斷是否已加載過數據,若是加載過,返回 name值
//沒有加載過, 則發送 SQL語句查詢加載數據, 而後返回 name值
} }
u 注意事項:通常情形: *.java-->*.class-->載入類加載器-->執行
延遲加載: javassist.jar/cglib.jar(生成新類型)-->類加載器-->執行
5.13 Session的 get和 load方法的區別
1)相同點:二者都是按「主鍵」作條件查詢。
2)不一樣點:①get是馬上加載; load是延遲加載。
②get返回的對象類型是實體類型; load返回的是動態生成的一個代理類 (動態代
理技術), 該代理類是實體類的子類。
③get未查到數據返回 null; load未查到數據拋出 ObjectNotFoundException異常。
u 注意事項: 若實體類用了 final修飾, 則破壞了延遲加載機制,那麼 load效果與 get

就徹底相同了。
5.14延遲加載的好處
1) 提升了內存的使用效率。
5.15案例: 測試延遲加載

 

2) 可使數據訪問下降併發量。
@Test
public void test1(){ Session session=HibernateUtil.getSession();
//load是延遲加載, foo沒有數據
Foo foo=(Foo)session.load(Foo.class, 1);//此時還沒去數據庫查詢

//session.close();//放這裏報錯, session關的過早 could not initialize proxy- no Session
System.out.println(foo.getName());//第一次調用屬性的 getter方法時觸發查詢
session.close();//放這裏不報錯,對象沒被回收
System.out.println(foo.getSalary()); }
@Test
public void test2(){ Session session=HibernateUtil.getSession();
Foo foo=(Foo)session.load(Foo.class, 1);//此時還沒去數據庫查詢
//類 org.tarena.entity.Foo$$EnhancerByCGLIB$$87e5f322, 由 cglib.jar生產
System.out.println(foo.getClass().getName()); session.close(); }
5.16案例:重構 NetCTOSS資費管理模塊
step1: 引入 Hibernate開發框架 (jar包和主配置文件)
step2: 採用 Hibernate操做 COST_CHANG表
1)添加實體類
private Integer id; //資費 ID private String name; //資費名稱 NAME
private Integer baseDuration; //包在線時長 BASE_DURATION
private Float baseCost; //月固定費 BASE_COST
private Float unitCost; //單位費用 UNIT_COST
privateStringstatus;//0:開通1:暫停; STATUS
private String descr; //資費信息說明 DESCR
private Date startTime; //啓用日期 STARTTIME
private Date creaTime;//建立時間 CREATIME
2)追加 Cost.hbm.xml
<hibernate-mapping><!-- <hibernate-mapping package="包名寫這也行"> -->
<class name="com.tarena.netctoss.entity.Cost" table="COST_CHANG" catalog="system">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="sequence">
<param name="sequence">COS T_SEQ_CHANG</param>
</generator>
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="baseDuration" type="java.lang.Integer">
<column name="BASE_DURATION" />
</property>
<property name="baseCost" type="java.lang.Float"><!--類型要和實體定義的相同 -->
<column name="BASE_COST" />
</property>
„„„„„„„„其餘略„„„„„„„„
</class>
</hibernate-mapping>

u 注意事項:
v 實體類和 hbm.xml必須保持一致!列名寫錯則會報:不能讀取實體類。
v junit測試右鍵點 Copy Trace查看錯誤列。
step3: 借用2.3節中 step6的 HibernateUtil類
step4: 按 CostDAO接口重構一個 DAO實現組件: HibernateCostDAOImpl
public void delete(int id) throws DAOException{Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Costcost=newCost(); cost.setId(id); session.delete(cost);
tx.commit(); session.close();
}
public List<Cost> findAll() throws DAOException{
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
String hql="from Cost"; Query query=session.createQuery(hql);
Listlist=query.list(); tx.commit(); session.close(); returnlist;
}
public List<Cost> findAll(int page, int rowsPerPage) throws DAOException{
Session session=HibernateUtil.getSession();//分頁查詢
Transaction tx=session.beginTransaction(); String hql="from Cost";
Query query=session.createQuery(hql);
int start=(page-1)*rowsPerPage;//設置分頁查詢參數
query.setFirstResult(start);//設置抓取記錄的起點,從0開始(第一條「記錄」)query.setMaxResults(rowsPerPage);//設置抓取多少條記錄
List list=query.list();//按分頁參數查詢
tx.commit(); session.close(); returnlist;
}
public Cost findById(Integer id) throws DAOException{
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Costcost=(Cost)session.load(Cost.class,id); tx.commit();
Stringname=cost.getName(); session.close(); returncost;
}
public Cost findByName(String name) throws DAOException{
//select* from COST_CHANG where NAME=?
String hql="from Cost where name=?";
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Query query=session.createQuery(hql);
query.setString(0, name);//注意 Hibernate賦值從 0開始, 即第一個問號
Cost cost=(Cost)query.uniqueResult();//適用於只有一行查詢結果返回
//若是返回記錄爲多條,則會報錯,多條用 query.list();
tx.commit(); session.close(); returncost;
}
public int getTotalPages(int rowsPerPage) throws DAOException{
//select count(*) from COST_CHANG
String hql="select count(*) from Cost";//類名
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Query query=session.createQuery(hql); Object obj=query.uniqueResult();

int totalRows=Integer.parseInt(obj.toString());
tx.commit(); session.close();
if(totalRows%rowsPerPage==0){ return totalRows/rowsPerPage;
}else{ return(totalRows/rowsPerPage)+1;
}
}
public void save(Cost cost) throws DAOException{
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();//下面的設置屬性建議寫到 Action中
cost.setStatus(" 1 "); cost.setCreaTime(new Date(System.currentTimeMillis()));
session.save(cost); tx.commit(); session.close();
}
public void update(Cost cost) throws DAOException{
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();//下面設置屬性建議寫到 Action中, 不
寫 DAO中, 由於 update有通用性可封裝, 到時沒法肯定 setXX方法
Cost cost1=(Cost)session.get(Cost.class, cost.getId());
cost1 .setName(cost.getName()); cost1 .setBaseDuration(cost.getBaseDuration());
cost1 .setUnitCost(cost.getUnitCost()); cost1 .setDescr(cost.getDescr());
session.update(cost1); tx.commit(); session.close();
}
step5: 修改 DAOFactory
private static CostDAO costDAO= new HibernateCostDAOImpl();
5.17 Java Web程序中如何用延遲加載操做(OpenSessionInView)
1) Java Web程序工做流程:
*.action-->Action-->DAO(延遲API)-->JSP(利用標籤或EL獲取數據,會觸發延
遲加載數據) -->生成響應 HTML頁面給瀏覽器。
2)基於上述緣由,在 DAO中不能關閉 Session, 須要將 Session關閉放到 JSP解析以後(把 Session的關閉延遲到 View組件運行完以後), 這種模式被稱爲 OpenSessionInView。
3) OpenSessionInView和 ThreadLocal:
使用 OpenSessionInView必須知足 Session的線程單例, 一個線程分配一個 Session,
在該線程的方法中能夠得到該 Session, 具體使用 ThreadLocal(一個線程爲 key的 Map)。
Hibernate支持的 Session線程單例, 配置文件中:
<property name="current_session_context_class">thread</property>
而後調用: sessionFactory.getCurrentSession();//自動實現線程單例
4) OpenSessionInView模式也能夠採用如下技術實現:
①利用 Struts2的攔截器(將關閉 session的操做寫在攔截器中)。
step1: 基於 ThreadLocal技術改造2.3中 step6的 HibernateUtil類
public class HibernateUtil{ private static SessionFactory sf;
private static ThreadLocal<Session> sessionLocal= new ThreadLocal<Session>();
static{//不用每次都加載配置信息, 因此放 static塊中, 不然耗費資源
Configuration conf=new Configuration();
conf.configure("/hibernate.cfg.xml");//加載主配置 hibernate.cfg.xml
sf=conf.buildSessionFactory();//獲取SessionFactory
}
/**同一個線程,只建立一個 session,建立出來後利用 ThreadLocal將 session與當
前線程綁定*/
public static Session getSession(){ Session session=sessionLocal.get();


if(session==null){//當前線程第一次調用,建立一個
session=sf.openSession();
sessionLocal.set(session);//將 session存取 ThreadLocal
}
return session;//若是能取到 session, 說明當前線程已經建立過 session}
/**把關閉 session也封裝一下*/
public static void closeSession(){ Session session=sessionLocal.get();
sessionLocal.set(null); if(session.isOpen()){
session.close();//關閉 session和釋放 ThreadLocal空間
}
}
/**簡單測試一下*/
public static void main(String[] args){
Session session1 = HibernateUtil.getSession();
Session session2 = HibernateUtil.getSession();
System.out.println(session1==session2);//true
}
}
step2:建立攔截器
public class OpenSessionInViewInterceptor extends AbstractInterceptor{//繼承抽象類
@Override
public String intercept(ActionInvocation arg0) throws Exception{
Session session=HibernateUtil.getSession();
//開啓事務, 或 Transaction tx=session.getTransaction(); tx.begin();
Transaction tx=session.beginTransaction();//等於以上兩步
System.out.println("開啓事務");
try{ arg0.invoke();//執行 action、 result-->jsp
if(!tx.wasCommitted()){ //當兩個action連續調用時,避免重複提交
tx.commit();//提交事務 System.out.println("提交事務"); }
return null;
}catch(Exceptione) { tx.rollback();//回滾事務 System.out.println("回滾事務");
e.printStackTrace(); throwe;//受AbstractInterceptor類影響,必須拋異常
}finally{ HibernateUtil.closeSession(); //關閉 session
System.out.println("關閉事務");
}
}
}
u 注意事項: 重複提交的狀況 redirectAction: add.action-->攔截器開啓事務
-->AddCostAction-->攔截器開啓事務-->ListCostAction-->cost_list.j sp-->攔截器
提交事務-->攔截器提交事務
step3: 在 NetCTOSS項目中的 struts-cost.xml中配置攔截器, 添加內容以下:
<interceptors>
<interceptor name="opensessioninview"
class="com.tarena.netctoss.interceptor.OpenSessionInViewInterceptor">
</interceptor>
<interceptor-stack name="opensessionStack">
<interceptor-ref name="opensessioninview"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack><!--默認的攔截器不能丟, 不然表單接收等不正常了-->
</interceptors>
<!--定義全局攔截器引用,若其餘配置文件也有全局攔截器,則覆蓋原來的,找最近的
(即在當前 struts-cost.xml中有效)相似於 Java中的局部變量-->


<default-interceptor-ref name="opensessionStack"></default-interceptor-ref>
step4:將5.16案例中 step4中的 HibernateCostDAOImpl類裏的全部方法中的開
啓事務、提交事務、關閉 Session所有刪除。
②利用 Filter過濾器。
public void doFilter(request,response,chain){
//前期處理邏輯
chain.doFilter(request,response);//調用後續 action,result組件
//後期處理邏輯。 關閉 session
③利用 Spring的 AOP機制。

6、關聯映射
關聯映射主要是在對象之間創建關係。開發者能夠經過關係進行信息查詢、添加、刪除和更新操做。
若是不使用 Hibernate 關聯關係映射, 咱們也能夠取到用戶對應的服務。
Account account= (Account)session.get(Account.class, 1);//取到用戶信息String hql="from Service s where s.accountId=1";
Query query= session.createQuery(hql);//取到用戶對應的服務
List<Item> list= query.list();
而 Hibernate提供的關聯映射, 更方便一些。
6.1一對多關係 one-to-many
step1:新建項目,導入 Hibernate開發包,借用 NetCTOSS項目中的實體: Account和 Service,配置 hibernate.cfg.xml和兩個實體的 hbm.xml映射文件。
step2: 一個 Account賬號對應多個 Service服務, 因此爲一對多關係。 所以爲 One方 Account 實體類添加 Set集合屬性, 以及對應的 get/set方法。
//追加屬性, 用於存儲相關聯的 Service信息
private Set<Service> services=new HashSet<Service>();
step3: 在 One方 Account.hbm.xml映射文件中, 加入 Set節點的映射
簡單說明:
<set name="屬性名">
<!--關聯條件, column寫外鍵字段,會默認的與ACCOUNT表的主鍵相關聯-->
<key column="指定關聯條件的外鍵字段"></key>
<!--指定採用一對多關係, class指定關聯的類型-->
<one-to-manyclass="要關聯的另外一方(N方)"/>
</set>
具體實現:
<!--描述 services屬性,採用一對多關係加載 service記錄-->
<!-- 是 list集合用<list name=""></list> set集合用<set name=""></set> -->
<set name="services">
<key column="ACCOUNT_ID"></key><!--ACCOUNT_ID是 Service表中字段-->
<one-to-many class="org.tarena.entity.Service"/>
</set>
step4: 借用5.17節4)中的 step1中的 HibernateUtil類step5: 新建 TestOneToMany.java類用於測試
@Test
public void test1(){ Session session=HibernateUtil.getSession();
Account account=(Account)session.load(Account.class, 1011);//第一次發送 SQL查詢
System.out.println(account.getRealName()); System.out.println(account.getIdcardNo());
/**顯示與當前賬號相關的 Service業務賬號, 之前的方式須要寫 hql:
String hql="from Service where ACCOUND_ID=1011";用了關聯映射則不用寫 hql了*/
Set<Service> services=account.getServices();//延遲加載, 第二次發送 SQL查詢
for(Service s: services) {

System.out.println(s.getId()+" "+s.getOsUsername()+" "+s.getUnixHost());
}
session.close();
}
6.2多對一關係 many-to-one
step1:新建項目,導入 Hibernate開發包,借用 NetCTOSS項目中的實體: Account和 Service,配置 hibernate.cfg.xml和兩個實體的 hbm.xml映射文件。
step2: 可多個 Service服務對應一個 Account賬號, 因此爲多對一關係。 所以爲 N方 Service 實體類添加 Account屬性, 以及對應的 get/set方法。
//追加屬性, 用於存儲關聯的 Account信息
privateAccount account;//已經包含 accountId了,原來的 accountId屬性刪!不然報錯
u 注意事項: Service實體原來的 accountId屬性刪,相應的 get/set方法也刪, Service
的映射文件對應的描述也刪!不然報錯: org.hibernate.MappingException: Repeated
column in mapping for entity: org.tarena.entity.Service column: ACCOUNT_ID
step3: 在 N方 Service.hbm.xml映射文件中描述 account屬性
簡單說明:
<many-to-one name="屬性名" class="要關聯的另外一方類型 Account"
column="關聯條件的外鍵字段"/> <!--指明外鍵字段,不寫主鍵-->
u 注意事項:此時沒有<setname="屬性名"></set>標籤。
具體實現:
<!--描述account,採用多對一關係加載-->
<many-to-one name="account" class="org.tarena.entity.Account"
column="ACCOUNT_ID"/> <!--指明外鍵字段,不寫主鍵-->
step4: 借用5.17節4)中的 step1中的 HibernateUtil類
step5: 新建 TestManyToOne.java類用於測試
@Test
public void test1(){ Session session=HibernateUtil.getSession();
Service service=(Service)session.load(Service.class, 2002);
//System.out.println(service.getId());//結果爲2002,但沒去查數據庫!主鍵傳啥顯示啥System.out.println(service.getOsUsername());//第一次 SQL查詢
System.out.println(service.getUnixHost());
//查看帳務帳號的真實名字、身份證、 Account_ID
System.out.println(service.getAccount().getId());//第二次 SQL查詢
System.out.println(service.getAccount().getRealName());
System.out.println(service.getAccount().getIdcardNo()); session.close();
}
6.3多對多關聯映射 many-to-many
數據庫設計是採用3張數據表,有一張是關係表。例如:
ADMIN_INFO-->ADMIN_ROLE<--ROLE
中間的關係表不用映射, 只映射兩端的表。 但如何對關係表進行操做呢?
答: Hibernate會自動的經過已經映射的表, 進行關係表操做。u 注意事項:
v 多對多關係是默認的級聯操做,可加 cascade屬性,可是加了以後至關於進入
一個循環,若刪除A數據,則全部表中有A的數據則都被刪除!所以通常都是採用一對多或多對一, 從而破壞這個循環。 詳情可看6.4節。
v 多對多關係必定有第三張表,間接實現多對多關係,兩表之間不可能有多對多
關係!
案例1: step1: 在 Admin實體類中添加一個 Set集合屬性
//追加屬性, 用於存儲相關聯的 Role信息
private Set<Role> roles=new HashSet<Role>();
step2:在Admin.hbm.xml中定義屬性的映射描述簡單說明:
<set name="關聯屬性名" table="中間的關係表">
<key column="關係表中與當前一方關聯的字段"></key>
<many-to-many class="關聯的另外一方類型"
column="關係表中與另外一方關聯的字段" />
</set>
具體實現:
<!--描述roles屬性,採用多對多加載Role對象的數據-->
<set name="roles" table="ADMIN_ROLE_CHANG">
<key column="ADMIN_ID"></key>
<many-to-many class="org.tarena.entity.Role" column="ROLE_ID" />
</set>
step3:新建TestManyToMany類,用於測試
@Test
public void testFind(){//測試查詢
Session session=HibernateUtil.getSession();
Admin admin=(Admin)session.get(Admin.class, 1001);
System.out.println(admin.getName()); System.out.println(admin.getEmail());
Set<Role> roles=admin.getRoles();//具備的角色
for(Rolerole:roles){ System.out.println(role.getId()+""+role.getName());
}
}
@Test
public void testAdd(){//測試添加管理員, 指定角色
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
Admin admin=newAdmin(); admin.setName("常1"); admin.setCode("chang1");
admin.setPassword("123123");
admin. setEnrollDate(new Date(System.currentTimeMillis()));
Role role1=(Role)session.load(Role.class, 20);//追加角色, 角色 ID爲20
Role role2=(Role)session.load(Role.class, 24);//追加角色, 角色 ID爲24
admin.getRoles().add(role1); admin.getRoles().add(role2);//把角色加入集合中
session.save(admin); tx.commit(); session.close();
}
@Test
public void testDelete(){ Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Admin admin=(Admin)session.get(Admin.class, 23);//查找 ID爲23的管理員
Role role1=(Role)session.get(Role.class,2);//查找 ID爲2的角色
admin.getRoles().remove(role1);//取消該管理員 ID爲2的角色
//增長角色 admin.getRoles().add(role1);//和上個方法相同
session.update(admin);//更新admin tx.commit(); session.close();
}

案例2: step1: 在 Role實體類中添加一個 Set集合屬性
//追加屬性, 用於存儲 admin信息
private Set<Admin> admins=new HashSet<Admin>();
step2:在Role.hbm.xml中定義屬性的映射描述
<!--採用多對多加載 admin信息-->
<set name="admins" table="ADMIN_ROLE_CHANG">
<key column="ROLE_ID"></key><!--與 admin.hbm.xml是相反的-->
<many-to-many column="ADMIN_ID" class="org.tarena.entity.Admin">
</many-to-many>
</set>
step3:在 TestManyToMany類中添加方法,用於測試
@Test
public void testFindRole(){//根據 role查詢相關 admin
Session session=HibernateUtil.getSession(); Role role=(Role)session.load(Role.class, 1);
System.out.println(role.getName());//查找ID爲1的角色,而後顯示名稱
for(Admin admin:role.getAdmins()){//顯示哪些 admin具備此角色
System.out.println(admin.getId()+" "+admin.getCode()+" "+admin.getName());
} session.close(); }
6.4關聯操做(查詢 join fetch/級聯 cascade)
1)查詢操做:創建關聯映射後,默認狀況下在調用關聯屬性的 getter方法時,會再發送
一個 SQL加載關係表數據。若是須要將關聯數據與主對象一塊兒加載(兩個 SQL查詢合成一個 SQL查詢), 能夠採用下面的方法:
①在 hbm.xml關聯屬性映射描述中(下例是在 Account.hbm.xml中),使用 lazy="false"
fetch="join"(不推薦使用!影響面太廣)
<!--關聯屬性的 lazy屬性默認爲 true, 主屬性默認爲 false, 主屬性也可加 lazy屬性-->
<!-- <set name="services" lazy="false" fetch="join">, lazy="false"關閉了延遲操做,與主對象一塊兒實例化。 fetch默認 select: 單獨發送一個 SQL查詢。 join: 錶鏈接用一個 SQL查詢。
不推薦用由於影響的映射範圍太廣, 推薦使用 HQL-->
<set name="services" lazy="false" fetch="join">
<key column="ACCOUNT_ID"></key><!--ACCOUNT_ID是 Service表中字段-->
<one-to-many class="org.tarena.entity.Service"/>
</set>
②編寫 HQL, 採用 join fetch關聯屬性方式實現。 (推薦使用!)
@Test
/**咱們指望: 當執行(Account)session.get(Account.class, 1001);語句, 取出 Account後,在屬性 services已經填充了全部的服務項 service */
public void test1(){ Session session=HibernateUtil.getSession();
Account account=(Account)session.get(Account.class, 101 1);
session.close();//當 hbm.xml中關聯屬性設置了 lazy="false", 在這裏關閉能正常執行Set<Service> services=account.getServices();//若 lazy="true"默認值, 則報錯
for(Service s: services) {
System.out.println(s.getId()+" "+s.getOsUsername()+" "+s.getUnixHost());
}
System.out.println(account.getRealName());
}

@Test /**TestOneToMany中添加方法*/
public void test2(){ Session session=HibernateUtil.getSession();
//String hql="fromAccount where id=?";//與 TestOneToMany中 test1效果同樣
//在下面的 hql中把須要關聯的屬性寫出來, 效果與在 hbm.xml加 fetch的效果同樣
String hql="fromAccount a join fetch a.services where a.id=?";//只執行一次 SQL查詢
Query query=session.createQuery(hql);
query.setInteger(0, 1011); Account account=(Account)query.uniqueResult();
System.out.println(account.getRealName()); System.out.println(account.getIdcardNo());
Set<Service> services=account.getServices();
for(Service s: services) {
System.out.println(s.getId()+" "+s.getOsUsername()+" "+s.getUnixHost());
}
session.close();
}
@Test /**TestManyToOne中添加方法*/
public void test2(){ Session session=HibernateUtil.getSession();
String hql="from Service s join fetch s.account where s.id=?";//只執行一次 SQL查詢
Query query=session.createQuery(hql);
query.setInteger(0, 2002);//Hibernate設置問號從 0開始
Service service=(Service)query.uniqueResult();
System.out.println(service.getOsUsername()); System.out.println(service.getUnixHost());
//查看帳務帳號的真實名字、身份證、 Account_ID
System.out.println(service.getAccount().getId());
System.out.println(service.getAccount().getRealName());
System.out.println(service.getAccount().getIdcardNo()); session.close();
}
2)級聯操做
當對主對象增刪改時, 能夠對關係屬性中的數據也相應的執行增刪改。
例如: session.delete(account);當 account中有 services且 services有值,則刪除 account
時, 相關聯的 services也被刪除。
級聯操做執行過程:首先級聯操做要開啓,而後判斷級聯屬性是否有數據,若無數
據則沒有級聯操做,如有數據則看是否有更改,有更改纔有級聯操做。
級聯操做默認是關閉的, 若是須要使用, 能夠在關聯屬性映射部分添加 cascade屬
性,屬性值有:①none:默認值,不支持級聯。②delete:級聯刪除。③save-update:級聯添加和更新④All:級聯添加、刪除、更新„„等。案例:級聯增長:
step1: 修改 Account.hbm.xml:
<set name="services" cascade="all">
<key column="ACCOUNT_ID"></key>
<one-to-many class="org.tarena.entity.Service"/>
</set>
step2:新建TestCascade類,用於測試級聯操做
@Test
public void testAdd(){//採用級聯方式添加一個 Account和兩個 Service
Account account=newAccount();//一個 Account, 簡單操做: 只把非空列設置上
account.setLoginName("chang"); account.setLoginPasswd("123");
account.setRealName("常"); account.setIdcardNo("1234567890");
account.setTelephone(" 123456789");
Service service1=new Service();//兩個 Service, 簡單操做: 只把非空列設置上
service1.setAccount(account);//因 accountId被刪了,因此這裏用 account,它裏面有 id
service1 .setOsUsername("chang1 "); service1 .setLoginPassword(" 1 1 1 ");
service1 .setUnixHost(" 1 92. 168.0.20"); service1 .setCostId(1);
Service service2=new Service();//同理
service2.setAccount(account); service2.setOsUsername("chang2");
service2.setLoginPassword("222"); service2.setUnixHost(" 192. 168.0.23");
service2.setCostId(2);
/**將 serivce1和 service2添加到 account.services集合中,不然不會級聯添加這兩個service, 同時 Hibernate會檢測,新增數據如果 services中的原有的, 則不往數據庫添加*/
account.getServices().add(service1); account.getServices().add(service2);
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
//hbm.xml添加 cascade="all", 則會對 account.service中的數據執行級聯添加
session.save(account); tx.commit(); session.close(); }
3) inverse屬性做用
默認狀況下,採用了級聯操做, Hibernate在執行 insert、 update、 deleted基本操做後,
還要執行 update關係字段的操做(即關係維護工做, 上例中維護的爲 ACCOUNT_CHANG表中的 ID和 SERVICE_CHANG表中的 ACCOUNT_ID)。
默認是關聯對象雙方都要負責關係維護。 因此在上例中, 控制檯在最後會有兩個
update語句, 由於當前添加了一個 Account和兩個 Service, 因此 One方要維護兩個 Service,即兩個 update語句。 若是數據量很大, 則要維護 N個 Service, 則有 N個 updata語句, 此時就會影響性能。
爲了使 update語句不出現, 能夠在 Account.hbm.xml中的級聯屬性加 inverse="true"
屬性,即當前一方放棄關係維護,將這項工做交給對方負責。
例如: <set name="services" inverse="true" cascade="all">...</set>
u 注意事項:遇到一對多、多對一關係映射時,把 inverse="true"屬性加到
one-to-many一方 (One方放棄, Many方維護)。 能起到必定的優化做用。
4)級聯刪除
在 TestCascade類中添加方法:
@Test
public void testDelete(){
/**Accountaccount=newAccount(); account.setId(id); 級聯刪除,不要採用
newAccount()方法,由於 new出來的 account,它的 services是空的,那麼將是單表操做,
將報錯:違反完整性約束*/
Session session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction();
Account account=(Account)session.load(Account.class, 500);//應該先查找
session.delete(account);//再刪除 tx.commit(); session.close();
}
Hibernate級聯刪除的缺點: delete是按 id(主鍵)一條一條刪的,不是按關係字段
刪的, 當數據量小時可用 Hibernate的級聯刪除, 簡單方便些。
可是,但當數據量大時, Hibernate的級聯刪除效率低,則不建議使用 Hibernate的
級聯刪除, 建議採用 HQL語句的方式, 例如:

delete fromAccount where id=?
delete from Service where account.id=?

u 注意事項:
v 級聯刪除,不寫 inverse="true",且數據庫中 SERVICE_CHANG表中的
ACCOUNT_ID爲 NOT NULL約束, 那麼程序最後會執行 update, 會設置ACCOUNT_ID=null,那麼將與數據庫衝突!報錯!因此,應當加上inverse="true"。
6.5繼承關係映射

能夠將數據表映射成具備繼承關係的實體類。 Hibernate提供了3方式的繼承映射。
1)將一個表映射成父類和子類: 採用<subclass>描述子類。
2)將子類表映射成父類和子類(無父類表):採用<union-subclass>描述。
3) 將父類表和子類表映射成父類和子類: 採用<joined-subclass>描述子類繼承關係映射。
<joined-subclass name="子類類型" table="子類表" extends="父類類型">
<key column="子類表與父類表關聯的字段"></key>
//子類中其餘屬性的映射 property元素
</joined-subclass>
4)案例:將父類表和子類表映射成父類和子類
step1: PRODUCT表爲父表, CAR表和 BOOK表都爲子表。 建表語句以下:
CREATE TABLE PRODUCT(
ID NUMBER(5) CONSTRAINT PRODUCT_ID_PK PRIMARY KEY,
NAME VARCHAR2(20),
PRICE NUMBER(15,2),
PRODUCT_PIC VARCHAR2(100) );
CREATE SEQUENCE product_seq;
CREATE TABLE BOOK(
ID NUMBER(5) CONSTRAINT BOOK_ID_PK PRIMARY KEY,
AUTHOR VARCHAR2(20),
PUBLISHING VARCHAR2(50),
WORD_NUMBER VARCHAR2(20),
TOTAL_PAGEVARCHAR2(20) );
CREATE TABLE CAR(
ID NUMBER(5) CONSTRAINT CAR_ID_PK PRIMARY KEY,
BRAND VARCHAR2(20),
TYPE VARCHAR2( 1 ),
COLOR VARCHAR2(50),
DISPLACEMENTVARCHAR2(20) );
u 注意事項:父表即把子表中相同的字段提取出來,做爲父表。如:書和汽車都
有ID、名字、價格、產品圖片。
step2: 建立實體類 Product、 Book和 Car, 其中 Book和 Car類須要繼承 Product。
例如: public class Book extends Product{ ...}、 public class Car extends Product{ ...}
step3:添加映射文件: Product.hbm.xml、 Book.hbm.xml、 Car.hbm.xml
1) Product.hbm.xml
<hibernate-mapping>
<class name="org.tarena.entity.Product" table="PRODUCT">
<id name="id" type="integer" column="ID">
<generatorclass="sequence"><!--指定序列-->
<param name="sequence">PRODUCT_SEQ</param>

</generator>
</id>
<property name="name" type="string" column="NAME"></property> „„其餘部分略
2) Book.hbm.xml
<hibernate-mapping><!--描述了 Book與 Product的描述信息-->
<!--name:指明當前子類。 table:哪一個表。 extends:繼承哪一個父類。 -->
<joined-subclassname="org.tarena.entity.Book" table="BOOK"
extends="org.tarena.entity.Product">
<key column="ID"></key><!-- BOOK表中哪一個字段與 PRODUCT表關聯 -->
<!-- 這裏不自動增加, Hibernate會自動的把 Product中的主鍵值拿過來-->
<property name="author" type="string" column="AUTHOR"></property>
„„其餘部分略
3) Car.hbm.xml
<hibernate-mapping><!--描述了 Book與 Product的描述信息-->
<joined-subclass name="org.tarena.entity.Car" table="CAR"
extends="org.tarena.entity.Product">
<key column="ID"></key><!--CAR表中哪一個字段與 PRODUCT表關聯 -->
<!-- 這裏不自動增加, Hibernate會自動的把 Product中的主鍵值拿過來-->
<property name="brand" type="string" column="BRAND"></property>
„„其餘部分略
step4:建立TestExtends類,用於測試繼承關係映射
@Test
public void testAddBook(){ Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction(); Book book=new Book();
book.setName("常的書");//設置 product屬性 book.setPrice(50);
book.setProductPic("a.jsp");
book.setAuthor("常");//設置 book屬性 book.setPublishing("BO出版社");
book.setTotalPage("20"); book.setWordNumber("10000");
session.save(book);//執行保存 tx.commit(); session.close();
}
@Test
public void testFindBook(){ Session session=HibernateUtil.getSession();
Bookbook=(Book)session.load(Book.class, 1); System.out.println(book.getName());
System.out.println(book.getPrice()); System.out.println(book.getAuthor());
System.out.println(book.getPublishing()); session.close();
}
@Test
public void testDeleteBook(){ Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Book book=(Book)session.get(Book.class, 3); session.delete(book);
tx.commit(); session.close();
}
@Test
public void testAddCar(){ Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
Car car=new Car();//product信息 car.setName("Q7"); car.setPrice(800000);

car.setProductPic("b.jsp");
car.setBrand("奧迪");//car信息 car.setType("J");//J轎車 K卡車
car.setColor("黑色"); car.setDisplacement("3.0");//排量
session.save(car);//執行保存 tx.commit(); session.close();
}
@Test
public void testFindAllBook(){ Session session=HibernateUtil.getSession();
String hql="from Book";//from car爲全部汽車, from product爲全部商品
Query query=session.createQuery(hql); List<Book> books=query.list();
for(Bookbook:books){ System.out.println(book.getId()+" "+book.getName());}
}


7、 Hibernate查詢方法
7.1 HQL查詢
Hibernate Query Language簡稱 HQL。
HQL語句是面向對象的一種查詢語言。 HQL針對 Hibernate映射以後的實體類型和屬性進行查詢 (若出現表名和字段名則爲錯誤的!)。
7.2 HQL和 SQL的相同點
1)都支持 select、 from、 where、 groupby、 orderby、 having子句„„。
2)都支持+、-、*、/、>、<、>=、<=、<>等運算符和表達式。
3)都支持in、 notin、 between...and、 isnull、 isnotnull、 like、 or等過濾條件。4)都支持分組統計函數count、 max、 min、 avg、 sum。
7.3 HQL和 SQL的不一樣點
1) HQL區分大小寫(除了關鍵字外)。
2) HQL使用類名和屬性名,不能使用表名和字段名。3) HQL不能使用 select*寫法。
4) HQL不能使用 join...on中的 on子句。
5) HQL不能使用數據庫端的函數。
u 注意事項: HQL中 selectcount(*)可使用。
7.4 HQL典型案例
1)案例1:一個主鍵的狀況
step1:借用以前的Account實體、 Account.hbm.xml、 hibernate.cfg.xml
step2:新建TestHQL類,用於測試HQL。查詢操做可不寫事務控制語句。
@Test //SQL: select*fromACCOUNT_CHANG
public void test1(){//查詢全部帳務帳號信息 String hql="fromAccount";
Sessionsession=HibernateUtil.getSession(); Query query=session.createQuery(hql);
List<Account> list=query.list();//若是查詢出多條結果
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo()); }
session.close();
}
@Test //SQL: select* fromACCOUNT_CHANG where REAL_NAME like?
public void test2() {//按真名模糊查詢
//多個條件也可繼續加 and XX=? or XX=?
String hql="fromAccount where realName like?";//方式一
//String hql="fromAccount where realName like:n";//方式二
Session session=HibernateUtil.getSession(); Query query=session.createQuery(hql);
query.setString(0, "zhang%");//方式一: 設置查詢參數, 從 0開始表示第一個?
//query.setString("n", "zhang%");//方式二: 給:n賦值, 就不用數問號是第幾個了
//zhang_表示以 zhang開頭的兩個字名字
List<Account> list=query.list();//若是查詢出多條結果
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo());
}
session.close();
}
@Test //SQL: select ID,REAL_NAME,IDCARD_NO fromACCOUNT_CHANG
public void test3(){//查詢部分字段方式一
String hql="select id,realName,idcardNo fromAccount";
Session session=HibernateUtil.getSession(); Query query=session.createQuery(hql);
/**部分查詢,默認採用 Object[]封裝一行記錄的字段值,數組的大小、順序和寫的 HQL 屬性的個數、順序一致!注意事項:只要是單獨列出的屬性,則返回的就是Object數組!*/
List<Object[]> list=query.list();
for(Object[] objs:list){//若寫成原實體 Account則報錯類型轉換異常
System.out.println(objs[0]+" "+objs[1]+" "+objs[2]);
}
session.close();
}
@Test //SQL: select ID,REAL_NAME,IDCARD_NO fromACCOUNT_CHANG
public void test4(){//查詢部分字段方式二
String hql="select newAccount(id,realName,idcardNo) fromAccount";
/**爲了支持它,須要在 Account實體中加構造方法,無參的也加上(不然影響其餘地方的使用)。或者寫個新的實體類也能夠。 */
Session session=HibernateUtil.getSession(); Query query=session.createQuery(hql);
//部分查詢, 採用指定的 Account的構造方法封裝一行記錄的字段值
List<Account> list=query.list();
for(Account a:list){//注意: 顯示其餘屬性將會是初始值
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo()); }
session.close();
}
step3: Account實體中加構造方法和無參構造方法
public Account() { }
public Account(Integer id,String realName,String idcardNo){
this.id=id; this.realName=realName; this.idcardNo=idcardNo;
}
step4:在Account.hbm.xml中添加 HQL語句, step5中會用到
<class name="org.tarena.entity.Account" table="ACCOUNT_CHANG" ></class>
<!--和 class是平級的! 一個 query標籤寫一個 HQL語句-->
<queryname="findAll"><!--起個名字-->
<! [CDATA[from Account]]><! --怕特殊符號影響語句,放 CDATA段中做爲純文本-->
</query>
step5:其餘測試
@Test
public void test5(){//將 HQL定義到 hbm.xml中
Session session=HibernateUtil.getSession();
//session.getNamedQuery()方法,會去 hbm.xml中找 HQL語句
Query query=session.getNamedQuery("findAll");
List<Account> list=query.list();//若是查詢出多條結果
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo()); }
session.close();
}


@Test
public void test6(){//分頁查詢
String hql="fromAccount"; Session session=HibernateUtil.getSession();
Query query=session.createQuery(hql);
//設置分頁參數,就會按分頁形式查詢
query.setFirstResult(0);
/**設置抓取記錄的起點(每頁的第一條記錄), 0表明第一條「記錄」,不是頁數*/
query.setMaxResults(5);//設置最大抓取數量
List<Account> list=query.list();//若是查詢出多條結果
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo()); }
session.close();
}
@Test
/** SQL: select s.ID,s.OS_USERNAME,s.UNIX_HOST,a.REAL_NAME,a.IDCARD_NO
from SERVICE_CHANG s join ACCOUNT_CHANG a on(a.ID=s.ACCOUNT_ID) */
public void test7(){//對象關聯查詢
String hql="";//只要是單獨列出的屬性則返回的就是 object數組!
//hql+="selects.id,s.osUsername,s.unixHost,a.realName,a.idcardNo";/**方式一*/
//hql+="from Service sjoin s.account a";//不能寫 on子句, 多對一
//hql+="fromAccount a join a.services s";//或反過來, 一對多
/**方式二:必定要有「屬性」才能"."點出來,若屬性是集合、數組,則它們裏面
沒有其餘屬性,不能「.」出來*/
hql+="select s.id,s.osUsername,s.unixHost, s.account.realName,s.account.idcardNo";
hql+="from Service s";
//由於不能寫 on子句, 再加上 Service中有 account屬性, 因此關聯屬性寫 s.account
Session session=HibernateUtil.getSession(); Query query=session.createQuery(hql);
List<Object[]> list=query.list();
for(Object[] objs:list){ System.out.println(objs[0]+" "+objs[1]+" "+objs[2]+" "
+objs[3]+""+objs[4]); }
session.close();
}
2)案例2:聯合主鍵的狀況
step1: 建立 PERSON表
CREATE TABLE PERSON(
FIRST_NAME VARCHAR2(20), LAST_NAME VARCHAR2(20), AGE NUMBER);
ALTER TABLE PERSONADD CONSTRAINT PERSON_KEY
PRIMARY KEY(FIRS T_NAME,LAS T_NAME);
step2:1)建立Person實體, Hibernate要求聯合主鍵要再封裝
//private String firstName; private String lastName;
private PersonKey id;//主屬性
private Integer age; „„get/set方法
2) 建立 PersonKey實體, 用做 Person實體的聯合主鍵
/**必須實現 Serializable不然 load、 get方法不能調用了,由於它們的第二個參數是Serializable類型*/
public class PersonKey implements Serializable{

private String firstName; private String lastName; „„get/set方法
step3: 添加 Person.hbm.xml映射文件
<class name="org.tarena.entity.Person" table="PERSON">
<!--聯合主鍵-->
<composite-id name="id" class="org.tarena.entity.PersonKey">
<!--主鍵自動生成這裏就不適合了,要經過程序操做-->
<key-property name="firstName" type="string" column="FIRST_NAME">
</key-property>
<key-property name="lastName" type="string" column="LAST_NAME">
</key-property>
</composite-id>
<property name="age" type="integer" column="AGE"></property>
</class>
7.5 Criteria查詢
1)基本使用方式:
Criteria c=session.createCriteria(實體類.class); List list=c.list();
u 注意事項:分組、太複雜的語句用不了。
2)案例:建立TestCriteria類,用於測試Criteria查詢(藉助之前的實體和映射)@Test
public void test1(){//沒有任何子句 Session session=HibernateUtil.getSession();
Criteria c=session.createCriteria(Account.class); List<Account> list=c.list();
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo()); }
session.close();
}
@Test
public void test2(){//模糊查詢 Session session=HibernateUtil.getSession();
Criteria c=session.createCriteria(Account.class);
//Criteria c1 =session.createCriteria(Service.class);//能夠有第二個表
//追加條件,都被封裝在了 Restrictions中
c.add(Restrictions.like("realName", "zhang%")); List<Account> list=c.list();
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo()); }
session.close();
}
@Test
public void test3(){//追加兩個條件
Session session=HibernateUtil.getSession();
Criteria c=session.createCriteria(Account.class);
//c.add(Restrictions.like("realName", "zhang%"));
c.add(//當條件多時, 就比較麻煩了
Restrictions.and( Restrictions.like("realName","zhang%"),
Restrictions.eq("idcardNo", "410381194302256528")) );
c.addOrder(Order.desc("id"));//追加排序 List<Account>list=c.list();
for(Account a:list) {
System.out.println(a.getId()+" "+a.getRealName()+" "+a.getIdcardNo());
}
session.close();
7.6 Native SQL原生 SQL查詢
1) 基本使用:
SQLQuery query=session.createSQLQuery(sql);
List<Object[]> list=query.list();//默認封裝成 Object數組
u 注意事項:一些特殊的函數 Hibernate沒法執行需使用原生 SQL,極其複雜的
操做也可用原生 SQL。
2)案例:建立 TestSQLQuery類,用於測試原生 SQL查詢(一樣藉助之前的實體和映射)
@Test
public void test1(){//沒有用到映射文件
String sql="select* fromACCOUNT_CHANG";
Session session=HibernateUtil.getSession();
SQLQuery query=session.createSQLQuery(sql);
query.setFirstResult(0);//分頁查詢 query.setMaxResults(5);
List<Object[]> list=query.list();
for(Object[]objs:list){ System.out.println(objs[0]+""+objs[1]+""+objs[2]); }
session.close();
@Test
public void test2(){//改變 test1中的封裝類型
String sql="select* fromACCOUNT_CHANG";
Session session=HibernateUtil.getSession();
SQLQuery query=session.createSQLQuery(sql);
/**改變封裝類型,利用該類型映射(映射描述將起做用),封裝一條記錄(全字段
的,部分字段不行) */
query.addEntity(Account.class); List<Account>list=query.list();
for(Accounta:list){ System.out.println(a.getId()+""+a.getRealName()); }
session.close();
}

 


8、 Hibernate高級特性

8.1二級緩存
二級緩存是 SessionFactory級別的。 由 SessionFactory對象負責管理。經過 Factory建立的 Session均可以訪問二級緩存。
二級緩存默認是關閉的。若是二級緩存打開,則先去一級緩存找對象,找不到則去二級緩存, 還找不到則訪問數據庫。
u 注意事項:
v 一級緩存和二級緩存都是存「單個對象」的!不能存集合、數組!
v Hibernate自己沒有提供組件,須要第三方提供的組件。有不少第三方組件,這
裏用 ehcache-1.2.3.jar。
8.2 二級緩存開啓方法及測試
step1: 引入 ehcache-1.2.3.jar, 在 src下添加 ehcache.xml配置文件
ehcache.xml配置文件說明:
<diskStore path="java.io.tmpdir"/><!--磁盤存儲路徑-->
<defaultCache <!--默認參數設置-->
maxElementsInMemory="2000" <! --存儲的最大對象個數-->
eternal="false" <!--緩存對象的有效期, true爲永久存在-->
timeToIdleSeconds="20" <!--空閒時間:某個對象空閒超過20秒則清出二級緩存-->
timeToLiveSeconds="120" <!--某個對象生存了120秒,則自動清出二級緩存-->
overflowToDisk="true"<! --當存儲個數超出設置的最大值時, 把超出對象存到磁盤-->
/>
step2:在hibernate.cfg.xml中添加開啓配置參數,指定緩存類型
<!--開啓二級緩存-->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!--指定二級緩存組件類型, 相似於 JDBC中不一樣的數據庫驅動類型-->
<property name="hibernate.cache.provider_class">
net.sf.ehcache.hibernate.EhCacheProvider
</property><!--在 ehcache-1.2.3.jar中 net.sf.ehcache.hibernate下找-->
step3:在須要緩存的實體類Account.hbm.xml中,添加
<cache usage="read-only" region="採用 ehcache.xml中哪組參數緩存"/>
<class name="org.tarena.entity.Account" table="ACCOUNT_CHANG">
<!--read-only:只讀。 read-write:讀寫。 region:指明用哪組參數緩存-->
<cache usage="read-only" region="sampleCache1"/>
<id name="id" type="integer" column="ID"></id>
„„其餘部分略
step4:建立TestSecondCache類,用於測試二級緩存
@Test
public void test1(){//查詢一次, 一級緩存的做用
Session session=HibernateUtil.getSession();
Account account=(Account)session.load(Account.class, 1010);
System.out.println(account.getRealName()); System.out.println(account.getIdcardNo());


Account account1=(Account)session.get(Account.class, 1010);
System.out.println(account1.getEmail()); session.close();
}
@Test
public void test2(){//若沒有開啓二級緩存, 則查詢二次, 由於是不一樣的 Session對象
test1(); System.out.println("-------"); test1();//開啓二級緩存後,只查詢一次 }
8.3二級緩存管理方法
1) SessionFactory.evict(class);//清除某一類型的對象2) SessionFactory.evict(class,id);//清除某一個對象
3) SessionFactory.evict(list);//清除指定的集合
8.4 二級緩存的使用環境
1)共享數據,數據量不大。
2) 該數據更新頻率低 (一更新就涉及到同步, 就要有性能開銷了)。
8.5查詢緩存
一級和二級緩存只能緩存單個對象。對於其餘類型的數據,例如:一組字段或一組對象集合、數組都不能緩存。這種特殊結果,能夠採用查詢緩存存儲。查詢緩存默認是關閉的!
8.6 查詢緩存開啓方法及測試
step1: 開啓查詢對象的二級緩存 (由於也是跨 Session的)。
step2: 在 hibernate.cfg.xml中設置開啓查詢緩存參數。
<!--開啓查詢緩存-->
<property name="hibernate.cache.use_query_cache">true</property>
step3:建立 TestQueryCache類,用於測試查詢緩存,並在執行 query.list()方法前,設置query.setCacheable(true);
@Test
public void test1(){ //當傳入的參數相同時, 只執行一次查詢。 不一樣時執行兩次查詢
show("zhang%"); System.out.println("-------"); show("zhang%");//一次查詢 }
private void show(String name){
String hql="from Account where realName like?";
/**看 SQL不看HQL, SQL相同則從查詢緩存中取,不同則訪問數據庫查詢*/
Session session=HibernateUtil.getSession(); Query query=session.createQuery(hql);
query.setString(0, name);
/**採用查詢緩存機制進行查詢(在開啓二級緩存的基礎上)!去緩存查找,執行過
這次SQL,將結果集返回。未執行過,去數據庫查詢,並將SQL和結果存入緩存*/
query.setCacheable(true); List<Account>list=query.list();//執行查詢
for(Accounta:list){ System.out.println(a.getId()+" "+a.getRealName()); }
session.close(); }
8.7 查詢緩存的使用環境
1)共享數據的結果集,結果集數據量不該太大(幾十到幾百條便可)。
2)查詢的結果集數據變化很是小(最好不要變化,幾天變一次還可接受,先清原來的緩存再從新載入)。

9、 Hibernate鎖機制
Hibernate提供了樂觀鎖和悲觀鎖機制, 主要用於解決事務併發問題。
9.1悲觀鎖
Hibernate認爲任何操做均可能發生併發,所以在第一個線程查詢數據時,就把該條記錄鎖住。 此時其餘線程對該記錄不能作任何操做 (即增刪改查四種操做都不能)。 必須等當前線程事務結束才能夠進行操做。
9.2 悲觀鎖的實現原理
Hibernate 悲觀鎖機制其實是採用數據庫的鎖機制實現。
數據庫中 SQL語句最後加 for update則把記錄直接鎖死, 其餘用戶增刪改查都不行, 只能等待: select* from TRAIN where id=1 for update;
Hibernate中 load重載方法: session.load(Train.class,1,LockMode.UPDATE);只能等待當前用戶提交或回滾, 若等待超時則報異常!
悲觀鎖的缺點: 處理效率很低。
9.3 悲觀鎖使用步驟及測試
step1:建立表 TRAIN(火車)
CREATE TABLE TRAIN(
ID NUMBER PRIMARY KEY, T_START VARCHAR2(20),
T_ENDVARCHAR2(20), T_TICKETNUMBER );
INSERT INTO TRAIN VALUES( 1 ,'beij ing','shanghai', 1 00);
step2: 建立 Train實體類
private int id; private String start; private String end; private int ticket; „„get/set step3: 添加 Train.hbm.xml映射文件
<class name="org.tarena.entity.Train" table="TRAIN" >
<id name="id" type="integer" column="ID"></id>
<property name="start" type="string" column="T_START"></property>
<property name="end" type="string" column="T_END"></property>
<property name="ticket" type="integer" column="T_TICKET"></property>
</class>
step4:建立 ThreadClient類,用於模擬一個用戶操做
public class ThreadClient extends Thread{//繼承 Thread
public void run(){//模擬購票操做
Session session=HibernateUtil.getSession();
Transaction tx=session.beginTransaction();
//Train train=(Train)session.load(Train.class, 1);//不加鎖時, 查詢出火車信息
Train train=(Train)session.load(Train.class,1 ,LockMode.UPGRADE);//設置悲觀鎖
if(train.getTicket()>= 1 ) {//判斷剩餘票數>=購買票數
try{//知足購票條件,進行購票操做
Thread.sleep(2000);//模擬用戶操做
} catch(InterruptedExceptione) { e.printStackTrace(); }

int ticket=train.getTicket()- 1 ;//將票數更新
train.setTicket(ticket); System.out.println("購票成功!");
}else{ System.out.println("票數不足,購買失敗!"); }
tx.commit();//持久對象, 提交就自動同步, 至關於執行了 update
session.close(); } }
step5:建立TestTrain類,用於模擬併發操做
public static void main(String[] args) {
ThreadClient c1=new ThreadClient(); c1.start();
ThreadClient c2=new ThreadClient(); c2.start();
/**若 ThreadClient類爲無鎖查詢,則顯示2個購買成功,但數據庫卻減小1張票。若
ThreadClient類加了悲觀鎖, 則顯示2個購買成功, 數據庫減小2張票。 }
9.4樂觀鎖
認爲發生併發概率很是小。 相同的記錄不一樣的用戶均可以查詢訪問, 當多我的都要修改該記錄時,只有第一個提交的用戶成功,其餘的會拋出異常,提示失敗!
9.5 樂觀鎖的實現原理
樂觀鎖機制是藉助於一個「版本」字段實現,當第一個更新用戶提交成功後, Hibernate
會自動將該「版本」字段值+1,當其餘用戶提交,若是版本字段小於數據庫中的值,則會拋出異常,提示失敗。若是不使用框架技術,那麼咱們須要手工作對比,使用 Hibernate框架後,Hibernate能夠幫助咱們作 version對比的操做。
9.6樂觀鎖使用步驟及測試
step1: 向 TRAIN表中添加一個 T_VERSION版本字段
ALTER TABLE TRAIN ADD T_VERSION NUMBER;
step2: 在 Train實體類中添加 version屬性, 以及對應的 get/set方法
private int version;//版本屬性
step3:在 Train.hbm.xml中定義版本字段映射
簡單說明:
<class name="org.tarena.entity.Train" table="TRAIN" optimistic-lock="version">
<!--採用「版本」機制,不寫也可,默認是version-->
<id name="" type="" column=""></id>
<version name="屬性名" type="類型" column="字段名"></version>
具體實現:
<class name="org.tarena.entity.Train" table="TRAIN" optimistic-lock="version">
<id name="id" type="integer" column="ID"></id>
<!--定義樂觀鎖的版本字段,注意順序-->
<version name="version" type="integer" column="T_VERSION"></version>
<property name=" start" type=" string" column="T_S TART"></property>
step4: 將9.3節 step4中的 ThreadClient類, 改成不加鎖的 load方法
Train train=(Train)session.load(Train.class, 1);
step5: 再次執行9.3節 step5中的 TestTrain類
執行結果:先提交的事務執行成功,後提交的事務執行失敗,報錯信息爲:
StaleObjectStateException: Row was updated or deleted by another transaction

 


10、其餘注意事項


10.1源碼服務器管理工具
從此工做中會使用到的源碼服務器 (或叫版本服務器) 管理工具:
1) CVS經常使用樂觀鎖機制 2) SVN經常使用樂觀鎖機制
3) VSS用的少悲觀鎖機制
10.2利用 MyEclipse根據數據表自動生成實體類、 hbm.xml
step1: 進入 DB Browser, 創建一個與數據庫的鏈接

 

 

 


step2: 新建工程, 右鍵工程-->MyEclipse-->Add Hibernate Capbilities
step3: 彈出添加 Hibernate開發環境界面:
第一個界面選擇 Hibernate版本, 而後點 Next;
u 注意事項: 第一個界面中 Select the libraries to add to the buildpath是選擇要導入的

Hibernate的 Jar包,有兩種選擇: MyEclipse自帶的「MyEcipse Libraries」,還有自
己導入的「User Libraries」,若是已經手動將全部 Jar包拷貝到了 lib下, 不用再導入
了。那麼咱們都不選,去掉「MyEcipseLibraries」前面的 checkbox的對勾。
第二個界面添加 hibernate.cfg.xml主配置文件(可默認), 而後點 Next;
第三個界面在 DB Driver下拉菜單中選擇第一步創建的鏈接, 而後點 Next;
第四個界面建立 HibernateSessionFactory類, 其實就是咱們寫的 HibernateUtil, 而後點擊
Finish。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


u 注意事項:主配置文件中:
v 對於MySql鏈接,鏈接字符串中如如有&符號,須要寫成&amp;
v <propertyname="myeclipse.connection.profile">
oracle.jdbc.driver.OracleDriver</property>爲連接名, 能夠刪掉。
step4:在工程中,新建一個package,用於存放實體和hbm.xml
step5: 進入 DB Browser選中須要生成的數據表, 右鍵選擇 Hibernate Reverse Engineering...
第一個界面選擇生成 hbm.xml和 POJO(實體類), 及其存放路徑, 而後點 Next;
第二個界面選擇 Type Mapping爲 Hibernat types (其餘設置可默認), 而後點 Next;
第三個界面選擇數據表,能夠設定映射的POJO類名(包名.類名)和主鍵生成方式,選擇字段能夠設置屬性名和映射類型 (要改, 不然不符合 Java命名規範), 最後點擊 Finish。

u 注意事項:第一個界面有個Createabstractclass,建立抽象類。意思爲:
public class Foo extends AbstractFoo implements java.io.Serializable{
//裏面爲構造方法, 沒有屬性和 getter/setter方法
public abstract class AbstractFoo implements java.io.Serializable{
//裏面爲屬性和 getter/setter方法

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


step6:完成後,實體類、 hbm.xml和hibernate.cfg.xml都已生成、修改好了。
u 注意事項:
v Hibernate生成的映射文件會有一些問題, 最好再修改一下。
v 能夠按「Ctrl」鍵選擇多個表,以後步驟相同,能夠同時建立多個表的 POJO類
和映射文件。
10.3根據實體類和 hbm.xml生成數據表
利用 Hibernate框架自帶的 hbm2ddl工具, 根據實體類和 hbm.xml生成數據表。step1: 在 hibernate.cfg.xml中添加開啓工具的設置
<!--根據實體類和 hbm.xml生成數據表-->
<property name="hbm2ddl.auto">update</property>
step2:在執行添加、查詢等操做時,會自動建立數據表,而後再執行操做。
10.4 Hibernate中分頁查詢使用 join fatch的缺點
使用 Hibernate分頁查詢時, 若語句中使用了 join fatch語句, 則由「假分頁」機制實現。
實際採用可滾動 ResultSet遊標實現的數據抓取。 最後的顯示結果同樣, 可是 SQL語句不一樣。
假分頁 SQL語句: select* from bill, 返回大量數據, 而後利用遊標 result.absolute(begin); 即跳着查詢, 而 result.next();爲下一條記錄。
真分頁 SQL語句: MySql: select* from bill limit?,?
Oracle: select... (select... rownum<?) where rownum>?
那麼假分頁的結果就是,一次性把數據所有取出來放在緩存中,比較適合小數據量,若是數據量大, 對內存壓力比較大。
因此, 建議 join fatch語句不要和分頁查詢一塊兒用!
「假分頁」詳情可看 JDBC筆記9.5節。
10.5 Hibernate的子查詢映射
1) 在映射文件中使用以下格式:
<property name="屬性名" type="映射類型" formula="(子查詢語句)"></property>
在對當前對象查詢時, Hibernate 會將子查詢執行結果給指定的屬性賦值。
2) formula使用注意事項:
①必須將子查詢語句用括號括起來。
②子查詢語句必須是 SQL語句。
③子查詢中使用的表必須用別名 (在查詢條件中沒有別名的字段都默認是當前映射
實體類的字段)。
④當使用了子查詢映射後,如 HQL語句: from Host,默認查詢全部字段和子查詢
結果。若是隻須要部分字段值,不須要子查詢結果,能夠採用 select單獨指定須要的字段屬性。
3)案例: 對於 NetCTOSS項目中的每臺服務器資費使用量的查詢
step1: 在實體類 Host中添加三個屬性, 並設置 set/get方法
private int id; private String name; private String location;
private Integer c1;//追加屬性, 用於存儲包月使用數量
private Integer c2;//追加屬性, 用於存儲套餐使用數量
private Integer c3;//追加屬性, 用於存儲計時使用數量
step2: 在映射文件 Host.hbm.xml中添加這三個屬性的描述(採用子查詢形式)
<!--採用子查詢映射將包月使用量結果給 c1,缺點:只要寫 from HOST_CHANG就會嵌入子查詢,默認查詢全部字段和子查詢結果,若只須要部分字段值,不要子查詢結果,能夠採用: select部分字段名 fromHOST_CHANG-->
<property name="c1" type="integer" formula="(select count(*) from SERVICE_CHANG s,COST_CHANG c where s.COST_ID=c.ID and s.UNIX_HOST=ID and c.COST_TYPE='1')">
</property><!-- s.UNIX_HOST不能寫死了, 把 ID賦給它, ID爲 HOST中的字段-->
<!-- 採用子查詢映射將套餐使用量結果給 c2 -->
<property name="c2" type="integer" formula="(select count(*) from SERVICE_CHANG s,COST_CHANG c where s.COST_ID=c.ID and s.UNIX_HOST=ID and c.COST_TYPE='2')">
</property>
<!--採用子查詢映射將計時使用量結果給 c3 -->
<property name="c3" type="integer" formula="(select count(*) from SERVICE_CHANG s,COST_CHANG c where s.COST_ID=c.ID and s.UNIX_HOST=ID and c.COST_TYPE='3')">
</property>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

26

相關文章
相關標籤/搜索