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();
 
原創內容 轉自請註明【 http://sishuok.com/forum/blogPost/list/2477.html#7149
視頻配套PPT,視頻地址【  Hibernate4實戰-獨家視頻課程 】
相關文章
相關標籤/搜索