事務概述
事務就是指作為單個邏輯工作單元執行的一組數據操作這些操作要麼必須全部成功要麼必須全部失敗以保證數據的一致性和完整性事務具有ACID屬性
原子性自然界最小的事務的內容要麼都做要麼都不做
一致性 事務結束後狀態一致系統狀態和業務的邏輯規則一致
隔離性 就是加鎖機制多個事務訪問同一個數據分別被隔開
持久性 一旦事務結束事務的結果被永久的保存下來
數據庫事務管理
數據庫事務的ACID特性是由關系數據庫管理系統(RDBMS)來實現的數據庫管理系統采用日志來保證事務的原子性一致性和持久性
數據庫管理系統采用鎖機制來實現事務的隔離性
Hibernate應用程序中的事務管理
Hibernate對JDBC進行輕量級的對象封裝Hibernate本身在設計時並不具備事務處理功能平時所用的Hibernate的事務只是將底層的JDBCTransaction或者JTATransaction進行一下封裝在外面套上Transaction和Session的外殼其實底層都是通過委托底層的JDBC或JTA來實現事務的調度功能
)Hibernate中使用JDBC事務
要在Hibernate中使用JDBC事務可以在hibernatecfgxml中指定Hibernate事務為JDBCTransaction注意如果不配置默認使用JDBC事務
<property name=hibernatetransactionfactory_class>
orghibernatetransactionJDBCTransactionFactory
</property>
Transaction tx=null
try{
tx=sessionbeginTransaction();
//執行持久化操作
mit();
}catch(Exception e){
if(tx!=null) txrollback();
throw e;
} finally{
sessionclose();
}
) Hibernate中使用JTA事務
JTA(Java Transaction API) 可以簡單的理解成跨數據庫的事務由應用JTA 容器實現使用JTATransaction需要配置hibernatetransactionfactory_class參數該參數缺省值是orghibernatetransaction JDBCTransactionFactory當使用JTATransaction時需要將該參數改成orghibernatetransactionJTATransactionFactory並配置jtaUserTransaction參數JNDI名(Hibernate在啟動JTATransaction時要用該值到JNDI的上下文Context中去找javaxtransactionUserTransaction)
javaxtransactionUserTransactin tx = contextlookup(jndiName);
try{
txbegin();
//多個數據庫的session操作;
//session…
//session…
mit();
}catch(Exception e){
txrollback(); throw e;
}
並發訪問控制
數據庫事務並發引起的問題:
丟失更新:你改的時候我也改我改的內容覆蓋你修改的或者改的內容回滾了沒有保存進去
髒讀讀到髒數據你讀取之後我才修改的你讀取的內容無效或者也是回滾我修改的一半你就讀取了最後我給回滾了
不可重復讀兩次查詢的內容不一樣第二次讀取的時候被修改
幻讀兩次查詢的內容不一樣在第二次查詢的時候發現了與第一次不一樣的內容可能原因是中間有人進行更改或者刪除
事務隔離級別:為了解決多個事務並發引發的問題讓用戶根據需要在事務的隔離性和並發性之間做合理的權衡數據庫系統提供了種事務隔離級別
讀未提交(Read Uncommitted)隔離級別最低
讀已提交(Read committed)一般情況下用這個
可重復讀(Repeatable Read)
串行化(Serializable)隔離級別最高所以並發問題都沒有了
數據庫系統采用不同的鎖類型來實現這中隔離級別具體實現過程對用戶是透明的用戶只要選擇合適的隔離級別就可以了
隔離級別越高越能保證數據的完整性和一致性但對並發性能的影響也越大對於大多數應用程序可以優先考慮隔離級別為Read Committed它能夠避免藏讀而且具有較好的並發性能
Hibernate的配置文件可以顯示的設置隔離級別每種隔離級別對於一個正整數
Read Uncommitted
Read Committed:
Repeatable Read:
Serializable:
<property name=nnectionisolation></property>
樂觀並發控制
當數據庫系統采用Read Committed隔離級別時仍不能防止不可重復讀和幻讀在可能出現這種問題的場合可以在應用程序中采用樂觀鎖和悲觀鎖方式來解決
樂觀鎖是假定當前事務操作數據庫資源時不會有其他事務同時訪問因此不做數據庫層次上的鎖定為了維護正確數據hibernate用version和timestamp來實現
)使用版本號進行版本控制
在持久化類中定義一個version屬性類型只能是整型的
在映射文件中添加<version>標簽注意一定要放在<id>元素後面
悲觀並發控制
悲觀鎖是假定當前事務操作數據資源時一定有其他事務同時訪問該數據資源所以先鎖定資源
一般實現方式是由數據庫來實現采用獨占鎖來鎖定資源使用get()load()是可以顯示指定鎖定模式LockModeUPGRADE
Sessionget(StudentclassLockModeUPGRADE);
配置Cp連接池
<! 啟用cp連接池 設置連接池提供的供應商 >
<property name=connectionprovider_class>nnectionCPConnectionProvider</property>
<! 最大連接數 >
<property name=cpmax_size></property>
<! 最小連接數 >
<property name=cpmin_size></property>
<! 每次請求連接的數目 >
<property name=cpacquire_increment></property>
<! 設置過期時間以秒為單位如果連接池中 處於空閒狀態的連接超過了這個時間該連接就會從連接池中刪除>
<property name=cptimeout></property>
<! 每個秒檢查連接池中的空閒連接 >
<property name=cpidle_test_period></property>
Hibernate不適合的場景
不適合OLAP(OnLine Analytical Processing聯機分析處理)以查詢分析數據為主的系統適合OLTP(online transaction processing聯機事務處理)
對於些關系模型設計不合理的老系統也不能發揮hibernate優勢數據量巨大性能要求苛刻的系統hibernate也很難達到要求 批量操作數據的效率也不高
)Hibernate 整合Struts
Hibernate框架主要使用在持久層中完成實體類的CRUD操作
) 泛型DAO模式Hibenate實現
)OpenSessionInView模式
. 案例分析
Hibernatecfgxml中要配置如下數據
<property name=nnectionisolation></property>
映射文件中配置
<version name=version></version>
@Test
//測試樂觀鎖添加數據版本號自動生成不用手動添加
publicvoid testAdd(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User u = new User();
usetName(李四);
usetBirthday(new Date());
sessionsave(u);
sessiongetTransaction(mit();
HibernateUtilclose();
}
@Test
//測試樂觀鎖更改數據版本號會自動加
publicvoid testupdate(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass);
usersetName(王五);
sessiongetTransaction(mit();
HibernateUtilclose();
}
@Test
//簡單模擬hibernate中的事務測試通不過會報錯transaction not successfully started但是實驗顯示第二次修改的值會保存到數據庫
publicvoid testtransaction(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass);
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass);
usersetName(aa);
usersetName(bb);
sessiongetTransaction(mit();
sessiongetTransaction(mit();
HibernateUtilclose();
}
@Test
//測試悲觀鎖在同一個資源類中做測試的時候一定記得把樂觀鎖的設置注釋掉尤其是映射文件中的版本標簽這是可以簡單做下修改改為property屬性接著做實驗
//result:這時的select語句與平時不同where條件多了一句:where user_id=? for update
publicvoid find(){
Session session = HibernateUtilgetSession();
sessionbeginTransaction();
User user = (User) sessionload(Userclass LockModeUPGRADE);
Systemoutprintln(usergetName()++usergetBirthday());
sessiongetTransaction(mit();
HibernateUtilclose();
}
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28267.html