HIbernate中的Session
Session是JAVA應用程式和Hibernate進行互動時使用的主要接口,它也是持久化操作核心API, 注意這裡的Session的含義,它與傳統意思上web層的HttpSession并沒有關系,Hibernate Session之與Hibernate,相當于JDBC Connection相對與JDBC。
Session對象是有生命周期的,它以Transaction對象的事務開始和結束邊界
Session作為貫穿Hibernate的持久化管理器核心,提供了衆多的持久化的方法,如save(), update ,delete ,find(Hibernate 3中已經取消了此方法)等,通過這些方法我們可以透明的完成對象的增删改查(CRUD– create read update delete),這裡所謂的透明是指,Session在讀取,建立和删除影射的實體對象的執行個體時,這一系列的操作将被轉換為對資料庫表中資料的增加,修改,查詢和删除操作。
SessionFactory負責建立Session,SessionFactory是線程安全的,多個并發線程可以同時通路一個SessionFactory 并從中擷取Session執行個體。而Session并非線程安全,也就是說,如果多個線程同時使用一個Session執行個體進行資料存取,則将會導緻Session 資料存取邏輯混亂.是以建立的Session執行個體必須在本地存取空上運作,使之總與目前的線程相關。
Session有以下的特點
1,不是線程安全的,應該避免多個線程共享同一個Session執行個體
2,Session執行個體是輕量級的,所謂輕量級:是指他的建立和删除不需要消耗太多資源
3,Session對象内部有一個緩存,被稱為Hibernate第一緩存,他存放被目前工作單元中加載的對象,每個Session執行個體都有自己的緩存。
Hibernate Session緩存被稱為Hibernate的第一級緩存。SessionFactory的外置緩存稱為Hibernate的二級緩存。這兩個緩存都位于持久層,它們存放的都是資料庫資料的拷貝。SessionFactory的内置緩存 存放中繼資料和預定義SQL, SessionFactory的内置緩存是隻讀緩存。
Hibernate Session緩存的三大作用:
1,減少資料庫的通路頻率,提高通路性能。
2,保證緩存中的對象與資料庫同步,位于緩存中的對象稱為持久化對象。
3,當持久化對象之間存在關聯時,Session 保證不出現對象圖的死鎖。
Session 如何判斷持久化對象的狀态的改變呢?
Session 加載對象後會為對象值類型的屬性複制一份快照。當Session 清理緩存時,比較目前對象和它的快照就可以知道那些屬性發生了變化。
Session 什麼時候清理緩存?
1,commit() 方法被調用時
2,查詢時會清理緩存,保證查詢結果能反映對象的最新狀态。
3,顯示的調用session 的 flush方法。
session 清理緩存的特例:
當對象使用 native 生成器 時 會立刻清理緩存向資料庫中插入記錄。
org.hibernate Interface Session
public interface Session extends Serializable
: 是一個Java application 和Hibernate之間主要的運作時接口,這是執行持久化服務的中心API
主要方法:
public Transaction beginTransaction() throws HibernateException
【1】:傳回和目前Session對象互相聯系的Transaction對象(表示在資料庫中重新開始一個事務)
public Transaction getTransaction()
: 傳回和目前session聯系的Transaction對象
public Connection connection close() throws HibernateExcepton
:結束目前的Session對象
public void clear()
:清空Session,清除所有儲存在目前Session緩存中的實體對象,終止所有正在執行的方法(eg: save() , update() ,delete() …..)
public Serializable save(Object object)throws HibernateException
對目前參數指定的對象進行持久化(系統會首先賦予參數對象一個辨別符OID),他相當于insert語句 後面在詳細介紹
public Connection connection() throws HibernateException
得到目前Session 中包含的Connection對象。
public boolean contains(Object object)
:判斷參數給出的對象(持久化類)是否在目前Session的緩存中
public void evict(Object object) throws HibernateException
:将參數給出的Object從目前Session對象類中删除,使這個對象從持久态變成遊離态,這種狀态的改變不會引起對資料庫的同步,後面詳細介紹
public Object load(Class theclass ,Serializable id) throws HibernateException
傳回第一個參數指定類對應的表中,第二個參數指定的行(第二個參數就是要取得對象的OID,他對應表中主鍵列的值)
public void update(Object object) throws HibernateException
:更新一個對象到資料庫中,後面在詳細介紹
public void delete (Object object)throws HibernateException
:從資料庫中删除和參數指定的對象對應的記錄
public Object get(Class class,Serializable id) throws HibernateException
:和load()方法一樣差別在于,如果資料庫表中沒有對應的記錄,get()方法傳回null,load()方法将報異常
Transaction
Transanction接口是Hibernate的資料庫事務接口,用于管理事務,他對底層的事務作出了封裝,使用者可以使用Transanction對象定義自己的對資料庫的原子操作,底層事務包括:JDBC API ,JTA(Java Transaction API)。。。。。
一個Transaction對象的事務可能會包括多個對資料庫進行的操作
org.hibernate Interface Transaction
public interface Transaction
常用方法:
public void commit() throws HibernateException
重新整理目前的Session以及結束事務的工作,這個方法将迫使資料庫對目前的事務進行送出
public void rollback() throws HibernateException
:強迫復原目前事務
public boolean isActive() throws HibernateException
: 這個事務是否存活
—————————————————————————————-
Session:當中包含一個Connection對象
Connection c =session.getConnection();
Session的緩存用于臨時儲存持久化的對象,等到一定時候,再将緩存中的對象儲存到資料庫中。
應用程式事務:如果一個Session中包含有多個Transaction(資料庫事務),這些Transaction的集合稱為應用程式事務
标準使用形式:
Configuration config=new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionfactory=config.buildSessionFactory();
Session session=sessionfactory.openSession();
Transaction tx=session.beginTransaction();
try {
session.save();
tx.commit();
} catch(Exception e) {
if(tx!=null) tx.rollback();
} finally {
session.close ();
}
保證session的線程安全
ThreadLocal,在很多種Session 管理方案中都用到了它.ThreadLocal 是Java中一種較為特殊的線程綁定機制,通過ThreadLocal存取的資料,總是與目前線程相關,也就是說,JVM 為每個運作的線程,綁定了私有的本地執行個體存取空間,進而為多線程環境常出現的并發通路問題提供了一種隔離機制,ThreadLocal并不是線程本地化的實作,而是線程局部變量。
也就是說每個使用該變量的線程都必須為該變量提供一個副本,每個線程改變該變量的值僅僅是改變該副本的值,而不會影響其他線程的該變量的值,ThreadLocal是隔離多個線程的資料共享,不存在多個線程之間共享資源,是以不再需要對線程同步。
請看一下代碼:
public class HibernateUtil {
public static final SessionFactory sessionFactory;
public static final ThreadLocal session = new ThreadLocal();
static{
try{
Configuration configuration=new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
}catch (Throwable ex){
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static Session currentSession() throws HibernateException{
Session s = (Session) session.get();
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}
不安全解釋,例如:Servlet 運作是多線程的,而應用伺服器并不會為每個線程都建立一個Servlet執行個體,也就是說,TestServlet在應用伺服器中隻有一個執行個體(在Tomcat中是這樣,其他的應用伺服器可能有不同的實作),而這個執行個體會被許多個線程并發調用,doGet 方法也将被不同的線程反複調用,可想而知,每次調用doGet 方法,這個唯一的TestServlet 執行個體的session 變量都會被重置
在代碼中,隻要借助上面這個工具類擷取Session 執行個體,我們就可以實作線程範圍内的Session 共享,進而避免了線上程中頻繁的建立和銷毀Session 執行個體。不過注意線上程結束時關閉Session。同時值得一提的是,新版本的Hibernate在處理Session的時候已經内置了延遲加載機制,隻有在真正發生資料庫操作的時候,才會從資料庫連接配接池擷取資料庫連接配接,我們不必過于擔心Session的共享會導緻整個線程生命周期内資料庫連接配接被持續占用。
對于Web程式而言,我們可以借助Servlet2.3規範中新引入的Filter機制,輕松實作線程生命周期内的Session管理(關于Filter的具體描述,請參考Servlet2.3規範)。Filter的生命周期貫穿了其所覆寫的Servlet(JSP也可以看作是一種特殊的Servlet)
及其底層對象。Filter在Servlet被調用之前執行,在Servlet調用結束之後結束。是以,在Filter 中管理Session 對于Web 程式而言就顯得水到渠成。下面是一個通過Filter 進行Session管理的典型案例:
Java代碼
public class PersistenceFilter implements Filter {
protected static ThreadLocal hibernateHolder = new ThreadLocal();
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
hibernateHolder.set(getSession());
try {
//......
chain.doFilter(request, response);
//......
} finally {
Session sess = (Session)hibernateHolder.get();
if (sess != null) {
hibernateHolder.set(null);
try {
sess.close();
} catch (HibernateException ex) {
throw new ServletException(ex);
}
}
}
}
}
通過在doFilter中擷取和關閉Session,并在周期内運作的所有對象(Filter鍊中其餘的Filter,及其覆寫的Servlet 和其他對象)對此Session 執行個體進行重用,保證了一個Http Request處理過程中隻占用一個Session,提高了整體性能表現。在實際設計中,Session的重用做到線程級别一般已經足夠,企圖通過HttpSession實作使用者級的Session重用反而可能導緻其他的問題。凡事不能過火,Session重用也一樣。
Hibernate中的Transaction
Hibernate是對JDBC的輕量級對象封裝,Hibernate本身是不具備Transaction處理功能的,Hibernate的Transaction實際上是底層的JDBC Transaction的封裝,或者是JTA Transaction的封裝,下面我們詳細的分析:
Hibernate可以配置為JDBCTransaction或者是JTATransaction,這取決于你在hibernate.properties中的配置:
引用
hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
如果你什麼都不配置,預設情況下使用JDBCTransaction,不管你準備讓Hibernate使用JDBCTransaction,還是JTATransaction,我的忠告就是什麼都不配,将讓它保持預設狀态,如下:
在下面的分析中我會給出原因。
一、JDBC Transaction
看看使用JDBC Transaction的時候我們的代碼例子:
Java代碼
Session session = sf.openSession();
Transaction tx = session.beginTransactioin();
...
session.flush();
tx.commit();
session.close();
這是預設的情況,當你在代碼中使用Hibernate的Transaction的時候實際上就是JDBCTransaction。那麼JDBCTransaction究竟是什麼東西呢?來看看源代碼就清楚了:
Hibernate2.0.3源代碼中的類
Java代碼
//net.sf.hibernate.transaction.JDBCTransaction:
public void begin(); throws HibernateException {
log.debug("begin");;
try {
toggleAutoCommit = session.connection();.getAutoCommit();;
if (toggleAutoCommit); session.connection();.setAutoCommit(false);;
}
catch (SQLException e); {
log.error("Begin failed", e);;
throw new TransactionException("Begin failed with SQL exception: ", e);;
}
begun = true;
}
這是啟動Transaction的方法,看到 connection().setAutoCommit(false) 了嗎?是不是很熟悉?
public void commit(); throws HibernateException {
if (!begun); throw new TransactionException("Transaction not successfully started");
log.debug("commit");
try {
if ( session.getFlushMode();!=FlushMode.NEVER ); session.flush();
try {
session.connection().commit();
committed = true;
}
catch (SQLException e){
log.error("Commit failed", e);
throw new TransactionException("Commit failed with SQL exception: ", e);
}
}
finally {
session.afterTransactionCompletion();
}
toggleAutoCommit();
}
這是送出方法,看到connection().commit() 了嗎?下面就不用我多說了,這個類代碼非常簡單易懂,通過閱讀使我們明白Hibernate的Transaction都在幹了些什麼?我現在把用Hibernate寫的例子翻譯成JDBC,大家就一目了然了:
Java代碼
Connection conn = …; <— session = sf.openSession();;
conn.setAutoCommit(false);; <— tx = session.beginTransactioin();;
… <— …
conn.commit();; <— tx.commit();; (對應左邊的兩句);
conn.setAutoCommit(true);;
conn.close();; <— session.close();;
看明白了吧,Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫無神秘可言,隻不過在Hibernate中,Session打開的時候,就會自動conn.setAutoCommit(false),不像一般的JDBC,預設都是true,是以你最後不寫commit也沒有關系,由于Hibernate已經把AutoCommit給關掉了,是以用Hibernate的時候,你在程式中不寫Transaction的話,資料庫根本就沒有反應。