Hibernate4實戰 之 第三部分:Hibernate的基本開發

1:瞬時(Transient) - 由new操作符創建,且尚未與Hibernate Session 關聯的對象被認定爲瞬時的。瞬時對象不會被持久化到數據庫中,也不會被賦予持久化標識(identifier)。 如果瞬時對象在程序中沒有被引用,它會被垃圾回收器銷燬。 使用Hibernate Session可以將其變爲持久狀態,Hibernate會自動執行必要的SQL語句。
 
2:持久(Persistent) - 持久的實例在數據庫中有對應的記錄,並擁有一個持久化標識。 持久的實例可能是剛被保存的,或剛被加載的,無論哪一種,按定義,它存在於相關聯的Session作用範圍內。 Hibernate會檢測到處於持久狀態的對象的任何改動,在當前操作單元執行完畢時將對象數據與數據庫同步。開發者不需要手動執行UPDATE。將對象從持久狀態變成瞬時狀態同樣也不需要手動執行DELETE語句。
 
3:脫管(Detached) - 與持久對象關聯的Session被關閉後,對象就變爲脫管的。 對脫管對象的引用依然有效,對象可繼續被修改。脫管對象如果重新關聯到某個新的Session上, 會再次轉變爲持久的,在脫管期間的改動將被持久化到數據庫。
 
通過Session接口來操作Hibernate
新增——save方法、persist方法
1:persist() 使一個臨時實例持久化。然而,它不保證立即把標識符值分配給持久性實例,這會發生在flush的時候。persist() 也保證它在事務邊界外調用時不會執行 INSERT 語句。這對於長期運行的帶有擴展會話/持久化上下文的會話是很有用的。
2:save() 保證返回一個標識符。如果需要運行 INSERT 來獲取標識符(如 "identity" 而非"sequence" 生成器),這個 INSERT 將立即執行,不管你是否在事務內部還是外部。這對於長期運行的帶有擴展會話/持久化上下文的會話來說會出現問題。
 
刪除——delete方法
修改——有四種方法來做,分別是:
1:直接在Session打開的時候load對象,然後修改這個持久對象,在事務提交的時候,會自動flush到數據庫中。
2:修改託管對象,可用update或merge方法
3:自動狀態檢測:saveOrUpdate方法
update和merge方法
1:如果數據庫裏面存在你要修改的記錄,update每次是直接執行修改語句;而merge是先在緩存中查找,緩存中沒有相應數據,就到數據庫去查詢,然後再合併數據,如果數據是一樣的,那麼merge方法不會去做修改,如果數據有不一樣的地方,merge才真正修改數據庫。
2:如果數據庫中不存在你要修改的記錄,update是報錯;而merge方法是當作一條新增的值,向數據庫中新增一條數據。
3:update後,傳入的TO對象就是PO的了,而merge還是TO的。
4:如果你確定當前session沒有包含與之具有相同持久化標識的持久實例,使用update()。如果想隨時合併改動而不考慮session的狀態,使用 merge()。換句話說,在一個新 session中通常第一個調用的是update()方法,以保證重新關聯脫管對象的操作首先被執行。
5:請注意:使用update來把一個TO變成PO,那麼不管是否修改了對象,都是要執行update sql語句的。
 
 
通常下面的場景會使用 update() 或 saveOrUpdate()
1:程序在第一個 session 中加載對象
2:該對象被傳遞到表現層
3:對象發生了一些改動
4:該對象被返回到業務邏輯層
5:程序調用第二個session的update()方法持久這些改動
 
saveOrUpdate方法做下面的事:
1:如果對象已經在本session中持久化了,不做任何事
2:如果另一個與本session關聯的對象擁有相同的持久化標識,拋出一個異常
3:如果對象沒有持久化標識屬性,對其調用 save()
4:如果對象的持久標識表明其是一個新實例化的對象,對其調用 save()。
5:如果對象是附帶版本信息的(通過 <version> 或 <timestamp>)並且版本屬性的值表明其是一個新實例化的對象,save() 它。
6:否則update()這個對象
 
merge做如下的事情
1:如果session中存在相同持久化標識的實例,用用戶給出的對象的狀態覆蓋舊有的持久實例
2:如果session中沒有相應的持久實例,則嘗試從數據庫中加載,或創建新的持久化實例
3:最後返回該持久實例
4:用戶給出的這個對象沒有被關聯到 session 上,它依舊是脫管的
 
按主鍵查詢
1:load方法:load的時候首先查詢一級緩存,沒有就創建並返回一個代理對象,等到使用的時候,才查二級緩存,如果二級緩存中沒有數據就查數據庫,如果數據庫中沒有,就拋例外
2:get方法:先查緩存,如果緩存中沒有這條具體的數據,就查數據庫,如果數據庫沒有值,就返回null,總之get方法不管用不用,都要拿到真實的數據
Hibernate實現按條件查詢的方式
1:最重要的按條件查詢的方法是使用Query接口,使用HQL
2:本地查詢(native sql):就是使用標準的sql,也是通過Query接口來實現
3:按條件查詢(Query By Criteria,QBC):使用動態的,面向對象的方式來創建查詢
4:按樣例查詢(Query By Example,簡寫QBE):類似我們自己寫的getByCondition
5:命名查詢:在hbm.xml中配置hql語句,在程序裏面通過名稱來創建Query接口
Query的list方法
一個查詢通常在調用 list() 時被執行,執行結果會完全裝載進內存中的一個集合,查詢返回的對象處於持久狀態。如果你知道的查詢只會返回一個對象,可使用 list() 的快捷方式 uniqueResult()。
Iterator和List
某些情況下,你可以使用iterate()方法得到更好的性能。 這通常是你預期返回的結果在session,或二級緩存(second-level cache)中已經存在時的情況。 如若不然,iterate()會比list()慢,而且可能簡單查詢也需要進行多次數據庫訪問: iterate()會首先使用1條語句得到所有對象的持久化標識(identifiers),再根據持久化標識執行n條附加的select語句實例化實際的對象。
 
外置命名查詢
可以在映射文件中定義命名查詢(named queries)。

java代碼:
<query name="javass">
<![CDATA[select Object(o) from UserModel o]]>
</query>
參數綁定及執行以編程方式完成:
List list = s.getNamedQuery("cn.javass.h3.hello.UserModel.javass").list();
注意要用全限定名加名稱的方式進行訪問
 
flush方法
每間隔一段時間,Session會執行一些必需的SQL語句來把內存中對象的狀態同步到JDBC連接中。這個過程被稱爲刷出(flush),默認會在下面的時間點執行:
1:在某些查詢執行之前
2:在調用org.hibernate.Transaction.commit()的時候
3:在調用Session.flush()的時候
涉及的 SQL 語句會按照下面的順序發出執行:
1. 所有對實體進行插入的語句,其順序按照對象執行save() 的時間順序
2. 所有對實體進行更新的語句
3. 所有進行集合刪除的語句
4. 所有對集合元素進行刪除,更新或者插入的語句
5. 所有進行集合插入的語句
6. 所有對實體進行刪除的語句,其順序按照對象執行 delete() 的時間順序
除非你明確地發出了flush()指令,關於Session何時會執行這些JDBC調用是完全無法保證的,只能保證它們執行的前後順序。 當然,Hibernate保證,Query.list(..)絕對不會返回已經失效的數據,也不會返回錯誤數據。
lock方法:也允許程序重新關聯某個對象到一個新 session 上。不過,該脫管對象必須是沒有修改過的。示例如:s.lock(um, LockMode.READ);
注意:lock主要還是用在事務處理上,關聯對象只是一個附帶的功能
 
獲取元數據
Hibernate 提供了ClassMetadata接口和Type來訪問元數據。示例如下:

java代碼:
ClassMetadata catMeta = sf.getClassMetadata(UserModel.class);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();
for (int i = 0; i < propertyNames.length; i++) {
System.out.println("name=="+propertyNames[i] + ", type==“
                      +propertyTypes[i]);
}
 
HQL介紹
Hibernate配備了一種非常強大的查詢語言,這種語言看上去很像SQL。但是不
要被語法結構 上的相似所迷惑,HQL是非常有意識的被設計爲完全面向對象的查
詢,它可以理解如繼承、多態 和關聯之類的概念。
看個示例,看看sql和HQL的相同與不同:
Sql:select    *      from tbl_user    where uuid=‘123’
HQL:select Object(o) from UserModel o where o.uuid=‘123’
HQL特點
1:HQL對Java類和屬性是大小寫敏感的,對其他不是大小寫敏感的。
2:基本上sql和HQL是可以轉換的,因爲按照Hibernate的實現原理,最終運行的還是sql,只不過是自動生成的而已。
3:HQL支持內連接和外連接
4:HQL支持使用聚集函數,如:count、avg、sum、min、max等
5:HQL支持order by 和 group by
6:HQL支持條件表達式,如:in、like、between等
select子句
1:直接返回對象集合,形如:select o from UserModel o
2:返回某個特定類型的集合,形如:select o.name from UserModel o
3:返回Object[],形如:select o.uuid,o.name from UserModel o
4:返回List,形如:select new List(o.uuid,o.name) from UserModel o
5:返回任意的對象,形如:select new cn.javass.h3.hello.A(o.uuid,o.name) from UserModel o ,這要求A對象有一個構造方法是傳入這兩個參數
6:返回Map類型,形如:select new Map(o.uuid as Id,o.name as N) from UserModel o ,返回的結果,以as後面的別名做map的key,對應的數據做值
 
from子句
1:直接from對象,形如: from UserModel
2:可以分配別名,形如:from UserModel as um  , as關鍵字可以省略
3:如果from後面有多個對象,形如:from UserModel,DepModel ,相當於多表聯合查詢,返回他們的笛卡爾積
 
聚集函數
1:受支持的有avg,sum,min,max,count
2:關鍵字 distinct 與all 也可以使用,它們具有與 SQL 相同的語義,比如:
select count(distinct o.name) from UserModel o
nwhere子句
1:如果前面沒有指派別名,那就直接使用屬性名
2:如果指派了別名,必須使用別名.屬性的方式
3:在where子句中允許使用的表達式包括大多數在 SQL中使用的表達式,包括:
(1)數學運算符 +,-,*,/
(2)二進制比較運算符 =, >=, <=, <>, !=, like
(3)邏輯運算符 and,or,not
(4)括號 ( ),表示分組
(5)in, not in, between, is null, is not null, is empty, is not empty, member of and not member of
(6)字符串連接符 ...||... or concat(...,...)
(7)current_date(), current_time(), and current_timestamp()
(8)second(...)、minute(...)、hour(...)、day(...)、month(...) 和 year(...)
(9)EJB-QL 3.0 定義的任何功能或操作符:substring(), trim(), lower(), upper(), length(),locate(), abs(), sqrt(), bit_length(), mod()
(10)coalesce() 和 nullif()
(11)str() 把數字或者時間值轉換爲可讀的字符串
(12)cast(... as ...),其第二個參數是某 Hibernate 類型的名字,以及 extract(... from ...),只要 ANSI cast() 和 extract() 被底層數據庫支持
(13)HQL index() 函數,作用於 join 的有序集合的別名。
(14)HQL 函數,把集合作爲參數:size(), minelement(), maxelement(), minindex(), maxindex(),還有特別的 elements() 和 indices 函數,可以與數量詞加以限定:some, all, exists, any, in。
(15)任何數據庫支持的 SQL 標量函數,比如 sign(), trunc(), rtrim(), sin()
(16)JDBC 風格的參數傳入 ?
(17)命名參數 :name,:start_date,:x1
(18)SQL 直接常量 'foo', 69, 6.66E+2, '1970-01-01 10:00:01.0'
(19)Java public static final 類型的常量 eg.Color.TABBY
group by 子句
1:對於返回聚集值的查詢,可以按照任何屬性進行分組
2:可以使用having子句
3:sql中的聚集函數,可以出現在having子句中
4:group by 子句與 order by 子句中都不能包含算術表達式
5:不能group by 某個實體對象,必須明確的列出所有的聚集屬性
 
order by 子句
查詢返回的列表(list)可以按照一個返回的類或組件(components)中的任何屬性進行排序,可選的 asc 或 desc 關鍵字指明瞭按照升序或降序進行排序。
 
子查詢
對於支持子查詢的數據庫,Hibernate 支持在查詢中使用子查詢。一個子查詢必須被圓括號包圍起來。
n連接(join)
1:Hibernate可以在相關聯的實體間使用join,類似於sql,支持inner join、left outer join、right outer join、full join(全連接,並不常用)。
2:inner join可以簡寫成join,left outer join 和right outer join在簡寫的時候可以把outer去掉。
with
通過 HQL 的 with 關鍵字,你可以提供額外的 join 條件。
如:from Cat as cat left join cat.kittens as kitten with kitten.bodyWeight > 10.0
fetch
可以要求立即返回關聯的集合對象,如:

java代碼:
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens
對於沒有關聯的實體,如何使用join呢?
對於沒有關聯的實體,想要使用join,可以採用本地查詢的方式,使用sql來實現,比如:
s.createSQLQuery("select um.*,dm.* from tbl_user2 um left join tbl_dep dm on um.age=dm.uuid")
.addEntity("um",UserModel.class).addEntity("dm",DepModel.class);
1:新增
2:load、 get
3:修改
4:按條件查詢
(1)傳入條件值的方法:?或 :名稱,索引從0開始
(2)給參數賦值
(3)返回對象
(4)返回多個屬性,形成Object[]
(5)getByCondition的Hibernate版實現
5:刪除
6:分頁
Query q = sess.createQuery("from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();
 
也可以使用你的數據庫的Native SQL語言來查詢數據。這對你在要使用數據庫的
某些特性的時候(比如說在查詢提示或者Oracle中的 CONNECT關鍵字),這是非常有
用的。這就能夠掃清你把原來直接使用SQL/JDBC 的程序遷移到基於 Hibernate應用
的道路上的障礙。
使用SQLQuery
對原生SQL查詢執行的控制是通過SQLQuery接口進行的,通過執行
Session.createSQLQuery()獲取這個接口。下面來描述如何使用這個API進行查詢。
標量查詢(Scalar queries)
s.createSQLQuery(“select uuid,name from tbl_user”).list();
它將返回一個Object[]組成的List,Hibernate會使用ResultSetMetadata來判定返回的標量值的實際順序和類型。你也可以使用scalar來明確指明類型,如:
s.createSQLQuery("select * from tbl_user").addScalar("id", LongType.INSTANCE)
id字段就明確是long型,當然你也可以指定很多個字段的類型。
實體查詢(Entity queries)
上面的查詢都是返回標量值的,也就是從resultset中返回的“裸”數據。下面展示如何通過addEntity()讓原生查詢返回實體對象。
(1) s.createSQLQuery("select * from tbl_user").addEntity(UserModel.class);
(2) s.createSQLQuery(“select uuid,userId from tbl_user2”).addEntity (UserModel.class);  //一定要把表的所有字段羅列出來
(3) s.createSQLQuery("select {um}.uuid as {um.uuid},{um}.name as {um.name} from tbl_user {um}").addEntity("um",UserModel.class);
功能跟第二個差不多,也要把表的所有字段都羅列出來
(4)簡單點的寫法:s.createSQLQuery("select * from tbl_user2 um")
                                       .addEntity("um",UserModel.class);
(5)添加條件的示例:
s.createSQLQuery("select * from tbl_user where uuid=? and name like ?").addEntity(UserModel.class).setString(0, "3").setString(1,"%na%");
 
命名Sql查詢
可以在映射文檔中定義查詢的名字,然後就可以象調用一個命名的 HQL 查詢一樣直接調用命名 SQL查詢.在這種情況下,我們不 需要調用 addEntity() 方法。
在hbm.xml中配置,示例如下:

java代碼:
<sql-query name="users">
<return alias="um" class="cn.javass.h3.hello.UserModel"/>
select um.name as {um.name},
um.age as {um.age},
um.uuid as {um.uuid}
from tbl_user um
where um.name like :name
</sql-query>
注意:因爲要返回一個對象,所以要把表的所有字段都羅列上,否則會報錯“列名無效”,其實是在反射向對象賦值的時候,從sql的返回中得不到這個數據。
程序裏面調用示例:Query q = s.getNamedQuery(um.getClass().getName()
                                 +".users").setString("name", "%n%");
 
命名Sql查詢--使用return-property
使用 <return-property> 你可以明確的告訴 Hibernate 使用哪些字段別名,這取代了使用 {}-語法 來讓 Hibernate 注入它自己的別名。
在hbm.xml中配置,示例如下:

java代碼:
<sql-query name="users">
<return alias="um" class="cn.javass.h3.hello.UserModel">
<return-property name="name" column="umName"></return-property>
<return-property name="uuid" column="uuid"></return-property>
<return-property name="age" column="age"></return-property>
</return>
select um.name as umName,
um.age as age,
um.uuid as uuid
from tbl_user um
where um.name like :name
</sql-query>
具有一個直觀的、可擴展的條件查詢API是Hibernate的特色。
創建一個Criteria 實例
org.hibernate.Criteria接口表示特定持久類的一個查詢。Session是 Criteria實例的工廠。

java代碼:
Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();
限制結果集內容
一個單獨的查詢條件是org.hibernate.criterion.Criterion 接口的一個實例org.hibernate.criterion.Restrictions類 定義了獲得某些內置Criterion類型的工廠方法。

java代碼:
List list = s.createCriteria(UserModel.class)
.add(Restrictions.eq("uuid", "3"))
.add(Restrictions.like("name", "%n%"))
.list();
約束可以按照邏輯分組,示例如下:
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
       )
).list();
對結果集排序
可以使用 org.hibernate.criterion.Order 來爲查詢結果排序。示例如下:

java代碼:
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();
1:假如有如下程序,需要向數據庫裏面加如100000條數據:

java代碼:
Session session = sessionFactory.openSession();
 Transaction tx = session.beginTransaction();
 for ( int i=0; i<100000; i++ ) {
 Customer customer = new Customer(.....);
 session.save(customer);
}
tx.commit();
session.close();
這個程序很顯然不能正常運行,會拋出內存溢出的例外。按照前面講過的原理,Hibernate的save方法是先把數據放到內存裏面,數據太多,導致內存溢出。
那麼該如何解決呢?
解決方案:
1:首先將 hibernate.jdbc.batch_size的批量抓取數量參數設置到一個合適值(比如,10 - 50 之間),同時最好關閉二級緩存,如果有的話。
2:批量插入,一個可行的方案如下:

java代碼:
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) {
//將本批數據插入數據庫,並釋放內存
session.flush();
session.clear();
}
}
批量更新的做法跟這個類似
1:默認的Hibernate是有緩存的,稱之爲一級緩存。
2:也可以使用StatelessSession,來表示不實現一級緩存,也不和二級緩存和查詢緩存交互
3:StatelessSession是低層的抽象,和底層JDBC相當接近

java代碼:
StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers") .scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {
Customer customer = (Customer) customers.get(0);
customer.updateStuff(...);
session.update(customer);
} tx.commit(); session.close();
 
 
視頻配套PPT,視頻地址【 Hibernate4實戰-獨家視頻課程 】
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章