1、總括
一、sessionFactory與session
(1)sessionFactory:一個數據庫對應一個factory,線程安全、單例,通常隨應用開啓和關閉。
(2)session:非線程安全,管理connection(池)
session對於sessionFactory的關係至關於conn對於jdbc的關係,但session不等同於connection。
(3)另外,通常是一個session對一個事務。
二、hibernate不適合的場景
(1)複雜查詢/大批量數據導出
(2)批量修改/刪除
(3)實體關係過於複雜
三、鏈接池與prepareStatement池的原理
鏈接池,在getConn與closeConn的時候,不是真正的建立和關閉,採用代理攔截,在get的時候,從池中拿,若是有則返回,沒有則建立一個返回,歸還的時候,若是池沒有滿,則放入池中,若是滿了,則真正關閉。
(補上示例代碼)
prepareStatement池的原理就是緩存ps語句,不讓其隨着conn的關閉而失效。步驟就是攔截ps的close方法,而後每次create的時候,從池中查找,若是有則返回,沒有則建立,每次close的時候,看池有沒有滿,若是有滿,則關閉,沒有則放入池中。
(補上示例代碼)
四、hibernate configure的配置
Configuration cfg=new Configuration();
cfg.addClass();
cfg.addFile(「xxx.hbm.xml」);
或者
Configuration cfg=new Configuration().configure("myhibernate.cfg.xml");
默認從classpath去讀取hibernate.properties和hibernate.cfg.xml
五、實體狀態
一旦事務提交,實體就變成detached狀態
2、關係映射
一、一對多
@oneToMany()
好比一個產品有多個價格,則在product裏面的一對多,要mappedby=「product」,告訴hibernate不要建立中間表,採用product的外鍵來映射
inverse=true,即本身無論理關係,就是在保持product和price的時候,product不去管理(set/add)本身有多少個price,讓price去setProduct。這樣子,就能夠減小update 多端表的外鍵值的sql。
@joinColumn:設置外鍵值
@primaryKeyJoinColumn:即多的一段的主鍵就是其與主錶鏈接的外鍵,其值就是主表的主鍵值
通常設置雙向關聯:
單向關聯會額外發sql更新,好比,User與Address,先saveAddress,再saveUser,而後再發sql更新address的user外鍵值
雙向關聯,user的inverse設爲true,而後先saveuser,在saveAddress
user的inverse爲true表示讓address來維護關係,即保存address的時候以下操做:
add.setUser(user);
user.getAddresses().add(add);
session.save(add);
二、一對一映射與組件映射
一對一映射,從屬類有本身的id,而組件映射從屬類沒有本身的id
OneToOne,好比User與Passport,每一個passport都有本身的證件號
組件映射,好比User與Address/Name(first/last)
一對一,constraint=true,告訴hibernate,passport引用了user的主鍵
outer-join爲true的話,即加載User時,left-outer-join了passport
false的話,分兩次查,一次查user,一次查passport
外鍵關聯,與一對多的外鍵關聯相似
三、繼承映射
(1)每一個子類一張表 union
每一個子類的表都有共同的字段,當父類的字段要該的時候,則要該全部子類。
好處:自動隱式多態
(2)子類+父類都有表, join
爲了不重複,父類抽出來一張表,子類查詢,left-outer-join
(3)一個繼承體系一張表,dicriminator
爲解決一、2的性能問題而引人
缺點是有字段空缺,但好處是一張表,性能好
四、查詢多態
polymorphism=explict,顯示多態,即在查詢的時候,要顯示指定是要從父類仍是從子類查詢
createQuery("from Dog").list();
createQuery("from Aminal").list();
from Object,是隱式的多態,implicit,默認查詢的時候會加載父類的全部子類
五、多對多映射
好比group與role,role與right,得雙向各自add值
3、查詢與緩存
0、DetachedCriteria
在無session的狀況下可用,可用脫離session存在,在執行時綁定session,可用在方法裏面傳參數。
通常是推薦用hql來查詢
from TUser,是返回TUser的list
select u.name from TUser u,返回的是List<Object[]>
不過select new TUser() 返回的是非託管的類集合
一、組合查詢
採用example的方法能夠減小if else的判斷
二、統計查詢
setProjection, projection.avg()/rowCount()/max()/min()/distinct
三、加載個別字段
List<Object[]>,select a,b,c from TUser
List<TUser> , select new TUser(a,b,c) from TUser
注意,後者加載出來的是非託管狀態的,默認不加載oid,保持的時候,能夠考慮merge
四、四種加載方式
(1)即時加載(immediate)
先加載實體,在發出N個sql去加載其餘關聯實體
(2)延遲加載(lazy)
加載實體通常屬性,而後按需加載關聯屬性。
延遲加載,必須在session開的狀態,並且是get的時候加載的。若是想在session關閉以前,強制加載全部集合的話,能夠在session關閉以前用hibernate.intialize(set);
屬性的延遲加載:
A、對同一個表創建不一樣粒度的實體映射,好比從user表拆出resume類
B、在select的時候限定加載屬性列表
C、property設置lazy=true
(3)預先加載(eager)
一條sql,outer join 關聯屬性一次性加載
(4)批量加載(batch)
能夠認爲與上面沒有多少關係,主要是設置batchsize,積累相似的sql到必定量再批量處理,好比select * from user where id=1/2/3/4/5,這5條累積一塊兒批量執行。
集合方式分無序的set、bag、map 與有序的list
其中bag容許存在重複的元素
排序的話:
A、在JVM內排序,comparator藉口
B、在db內排序,order by (針對set、bag、map)
五、session.get與load的區別
get的話,返回真實實體,若是沒有找到則返回null,只查一級緩存
load的話,若是lazy爲true,返回的是代理類,沒有找到則拋出ObjectNotFound異常,會先查一級緩存,再查二級緩存
六、iterate與find的區別
iterate的話,典型的1+N,取id集合,而後去查緩存
find的話,取全部屬性,而後放入緩存
七、fetchtype
若是爲select的話,則會發一條sql加載集合,批量加載從屬屬性
若是爲join的話,left outer join, lazy失效
八、一級緩存與二級緩存
事務範圍內緩存,一級緩存(session),隨事務結束而結束
進程範圍/應用範圍緩存,二級緩存(session factory),緩存生命週期與sessionFactory的生命週期一致,適合較少被修改的緩存,非併發的訪問。如ehcache
集羣範圍內的緩存,如memcache
關聯對象的緩存策略:
set中配置cache-usage=read-only,只緩存數據索引(即id)
在one中配置cache-usage=read-write,能夠緩存集合中的實體
九、緩存管理
A、一級緩存
寫入:save、update、saveOrupdate、load、get、list、iterate、lock方法都會向緩存中存對象
更新:update、saveOrupdate,對於經過sql或query更新的,須要本身更新緩存
讀取:get、load、iterate,(query.list不會讀一級緩存,只寫入)
刪除:手動刪除,或者session關閉的時候自動刪除
B、二級緩存
寫入:save、update、saveOrupdate、load、get、list、query、Criteria方法都會填充二級緩存。session.save(user) 若是user主鍵使用「native」生成,則不放入二級緩存.
更新:update、saveOrupdate、delete,對於經過sql或query更新的,須要本身更新緩存
讀取:get、load、iterate會從二級緩存中取數據。若是經過ehcache緩存組件爲Hibernate配置查詢緩存(query cache[hibernate 3.x以上版本]),這Query.list會每次查詢的過程當中先訪問二級緩存中的查詢緩存,若是沒有再執行SQL語句,查詢返回的結果會分別保存在一,二級緩存中。
刪除:手工清除或者sessionFactory關閉的時候
對於sql的查詢,都是直接從數據庫查,只不過查出來後根據ID放入緩存,只有根據id獲取的時候,才查緩存。(於是,此時1+N纔是發揮威力的時候)
若是配置了query cache的話,能夠對整個查詢條件的結果集進行緩存。
批量導出的時候,採用純sql,不走緩存,獲取較高性能。
-----------
當應用程序調用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調用查詢接口的list()、iterate()或filter()方法時,若是在Session緩存中還不存在相應的對象,Hibernate就會把該對象加入到第一級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新數據庫。
Session爲應用程序提供了兩個管理緩存的方法:
evict(Object obj):從緩存中清除參數指定的持久化對象。
clear():清空緩存中全部持久化對象。
--------
Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無做用。爲此,Hibernate提供了針對條件查詢的Query Cache。
Hibernate的二級緩存策略(根據ID緩存)的通常過程以下:
1) 條件查詢的時候,老是發出一條select * from table_name where …. (選擇全部字段)這樣的SQL語句查詢數據庫,一次得到全部的數據對象。
2) 把得到的全部數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,若是配置了二級緩存,那麼從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
4) 刪除、更新、增長數據的時候,同時更新緩存。
---------
十、二級緩存併發策略
(1)read-only
(2)non-strict-read-write(使用更新頻率低)
(3)read-write,讀提交,基於timestamp/version
(4)transactional
4、持久化與事務
一、openSession與getCurrentSession
openSession,永遠建立一個新的session,須要自動關閉conn
getCurrentSession,查找上下文,若是有session,則與之連在一塊兒,會自動關閉conn
二、save、update、delete、lock
save,查緩存,若是存在,判斷數據是否變化,沒有則直接返回,如有變化,則進行髒數據檢查,而後update
若是save查緩存不存在,則insert
save方法歸入一級緩存,不歸入二級緩存
update,根據key去session緩存中查找。在事務提交/flush的時候發出updatesql
saveOrUpdate
根據version/timestamp判斷
delete:先查找再刪除。對於批量刪除,比較費勁,由於若是直接一條sql查找的話,會使緩存與db不一樣步
但後來也提供bulk delete/update 方法,但仍是能會不一樣步,獲得的是過時的緩存
lock:將一個已經讀入內存但未通過更改的遊離態的對象與session關聯起來,以便實現延遲加載。
三、merge
對於沒有oid的會insert,用於將不在session中管理時的對象變成託管狀態
四、flush
與數據庫同步,累積必定量執行,默認在事務提交的時候執行
五、自定義持久化
好比不想更新的時候更新全部字段,如今可使用dynamic update/insert,之前是能夠自定義sql-insert或者sql-update或者sql-delete來指定。
六、JDBC事務與JTA事務
hibernate的openSession,在初始化數據庫鏈接的時候,setAutoCommit爲false
JDBC事務依託於driver的connection,屬於session生命週期內
JTA事務,依託於JTA容器,跨多個session,甚至多個sessionFactory
七、併發異常與數據庫讀寫策略
併發異常:主要是一類更新,二類更新,髒讀、不可重複讀、幻讀
讀寫策略:讀未提交、讀提交(大多數據庫默認)、重複讀、串行讀
5、其餘
一、對象惟一性的判斷
(1)主鍵,不建議用業務主鍵來當db主鍵
(2)關鍵業務屬性(推薦,由於主鍵可能有null的狀況)
二、判斷是insert仍是update
-->經過unsaved-value,其值默認爲null,好比主鍵,若是對象的id是null,則insert,不然則update
三、回調與攔截機制
(1)實現lifecycle接口
(2)validatable接口,實現非業務的數據驗證
上面兩種侵入性強,能夠改用interceptor
==>通常用在audit用,不在攔截裏面作業務相關操做,也不在裏面對session進行實例化操做。
onSave/FlushDirty/postFlush
6、ThreadLocal的session
參考HibernateUtil類
public
final
class
HibernateUtil {
private
static
SessionFactory sessionFactory;
private
static
ThreadLocal session
=
new
ThreadLocal();
private
HibernateUtil() {
}
static
{
Configuration cfg
=
new
Configuration();
cfg.configure();
sessionFactory
=
cfg.buildSessionFactory();
}
public
static
Session getThreadLocalSession() {
Session s
=
(Session) session.get();
if
(s
==
null) {
s
=
getSession();
session.set(s);
}
return
s;
}
public
static
void
closeSession() {
Session s
=
(Session) session.get();
if
(s
!=
null) {
s.close();
session.set(null);
}
}
public
static
SessionFactory getSessionFactory() {
return
sessionFactory;
}
public
static
Session getSession() {
return
sessionFactory.openSession();
}
public
static
void
add(Object entity) {
Session s
=
null;
Transaction tx
=
null;
try
{
s
=
HibernateUtil.getSession();
tx
=
s.beginTransaction();
s.save(entity);
tx.commit();
}
finally
{
if
(s
!=
null)
s.close();
}
}
public
static
void
update(Object entity) {
Session s
=
null;
Transaction tx
=
null;
try
{
s
=
HibernateUtil.getSession();
tx
=
s.beginTransaction();
s.update(entity);
tx.commit();
}
finally
{
if
(s
!=
null)
s.close();
}
}
public
static
void
delete(Object entity) {
Session s
=
null;
Transaction tx
=
null;
try
{
s
=
HibernateUtil.getSession();
tx
=
s.beginTransaction();
s.delete(entity);
tx.commit();
}
finally
{
if
(s
!=
null)
s.close();
}
}
public
static
Object get(Class clazz, Serializable id) {
Session s
=
null;
try
{
s
=
HibernateUtil.getSession();
Object obj
=
s.get(clazz, id);
return
obj;
}
finally
{
if
(s
!=
null)
s.close();
}
}
}
實現session在單個線程裏面的共享,但要注意的是當線程結束的時候,session就關閉了。
在線程裏面,session也實現自身的延遲加載,即在發生對數據庫操做的時候,才從數據庫獲取conn
在web中的應用,通常一個http請求,一個線程,佔用一個session。長session是指在多個request中重複使用同一個session,最後使用完才關閉,這個比較危險,要慎重。