- 論壇徽章:
- 0
|
Hibernate對JDBC進(jìn)行了輕量級的對象封裝,Hibernate本身在設(shè)計(jì)時(shí)并不具備事務(wù)處理功能,平時(shí)所用的Hibernate的事務(wù),只是將底層的JDBCTransaction或者JTATransaction進(jìn)行了一下封裝,在外面套上Transaction和Session的外殼,其實(shí)底層都是通過委托底層的JDBC或JTA來實(shí)現(xiàn)事務(wù)的調(diào)度功能。
2.2. Hibernate中使用JDBC事務(wù):
要在Hibernate中使用事務(wù),可以配置Hibernate事務(wù)為JDBCTransaction或者JTATransaction,這兩種事務(wù)的生命周期不一樣,可以在hibernate.cfg.xml中指定使用的是哪一種事務(wù)。以下配置為使用JDBC事務(wù)。注:如果不進(jìn)行配置,Hibernate也會(huì)默認(rèn)使用JDBC事務(wù)。
……
org.hibernate.transaction.JDBCTransactionFactory
……
Hibernate 使用JDBC transaction處理方式如下所示:
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
2.3. Hibernate中使用JTA事務(wù):
JTA(java Transaction API)是事務(wù)服務(wù)的JavaEE解決方案。本質(zhì)上,它是描述事務(wù)接口的JavaEE模型的一部分。
JTA具有的3個(gè)接口:UserTransaction接口、TransactionManager接口和Transaction接口,這些接口共享公共的事務(wù)操作。UserTransaction能夠執(zhí)行事務(wù)劃分和基本的事務(wù)操作,TransactionManager能夠執(zhí)行上下文管理。
在一個(gè)具有多個(gè)數(shù)據(jù)庫的系統(tǒng)中,可能一個(gè)程序?qū)?huì)調(diào)用幾個(gè)數(shù)據(jù)庫中的數(shù)據(jù),需要一種分布事務(wù),或者準(zhǔn)備用JTA來管理Session的長事務(wù),那么就需要使用JTATransaction。
在hibernate.cfg.xml中配置JTA事務(wù)管理:
……
org.hibernate.transaction.JTATransactionFactory
……
下面是一個(gè)實(shí)際應(yīng)用的JTA示例:
// BMT(bean管理事務(wù)) idiom with getCurrentSession()
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work on Session bound to transaction
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
在CMT方式下,事務(wù)聲明是在session bean的部署描述符中,而不需要編程。 因此,代碼被簡化為:
// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
3. 多個(gè)事務(wù)并發(fā)引起的問題:
3.1. 第一類丟失更新:撤消一個(gè)事務(wù)時(shí),把其它事務(wù)已提交的更新的數(shù)據(jù)覆蓋了。
3.2. 臟讀:一個(gè)事務(wù)讀到另一個(gè)事務(wù)未提交的更新數(shù)據(jù)。
3.3. 幻讀:一個(gè)事務(wù)執(zhí)行兩次查詢,但第二次查詢比第一次查詢多出了一些數(shù)據(jù)行。
3.4. 不可重復(fù)讀:一個(gè)事務(wù)兩次讀同一行數(shù)據(jù),可是這兩次讀到的數(shù)據(jù)不一樣。
3.5. 第二類丟失更新:這是不可重復(fù)讀中的特例,一個(gè)事務(wù)覆蓋另一個(gè)事務(wù)已提交的更新數(shù)據(jù)。
4. 事務(wù)隔離級別:
為了解決多個(gè)事務(wù)并發(fā)會(huì)引發(fā)的問題。數(shù)據(jù)庫系統(tǒng)提供了四種事務(wù)隔離級別供用戶選擇。
o Serializable:串行化。隔離級別最高
o Repeatable Read:可重復(fù)讀。
o Read Committed:讀已提交數(shù)據(jù)。
o Read Uncommitted:讀未提交數(shù)據(jù)。隔離級別最差。
數(shù)據(jù)庫系統(tǒng)采用不同的鎖類型來實(shí)現(xiàn)以上四種隔離級別,具體的實(shí)現(xiàn)過程對用戶是透明的。用戶應(yīng)該關(guān)心的是如何選擇合適的隔離級別。
對于多數(shù)應(yīng)用程序,可以優(yōu)先考慮把數(shù)據(jù)庫系統(tǒng)的隔離級別設(shè)為Read Committed,它能夠避免臟讀,而且具有較好的并發(fā)性能。
每個(gè)數(shù)據(jù)庫連接都有一個(gè)全局變量@@tx_isolation,表示當(dāng)前的事務(wù)隔離級別。JDBC數(shù)據(jù)庫連接使用數(shù)據(jù)庫系統(tǒng)默認(rèn)的隔離級別。在Hibernate的配置文件中可以顯示地設(shè)置隔離級別。每一種隔離級別對應(yīng)著一個(gè)正整數(shù)。
Read Uncommitted: 1
Read Committed: 2
Repeatable Read: 4
Serializable: 8
在hibernate.cfg.xml中設(shè)置隔離級別如下:
2
設(shè)置之后,在開始一個(gè)事務(wù)之前,Hibernate將為從連接池中獲得的JDBC連接設(shè)置級別。需要注意的是,在受管理環(huán)境中,如果Hibernate使用的數(shù)據(jù)庫連接來自于應(yīng)用服務(wù)器提供的數(shù)據(jù)源,Hibernate不會(huì)改變這些連接的事務(wù)隔離級別。在這種情況下,應(yīng)該通過修改應(yīng)用服務(wù)器的數(shù)據(jù)源配置來修改隔離級別。
5. 并發(fā)控制:
當(dāng)數(shù)據(jù)庫系統(tǒng)采用Red Committed隔離級別時(shí),會(huì)導(dǎo)致不可重復(fù)讀和第二類丟失更新的并發(fā)問題,在可能出現(xiàn)這種問題的場合?梢栽趹(yīng)用程序中采用悲觀鎖或樂觀鎖來避免這類問題。
5.1. 樂觀鎖(Optimistic Locking):
樂觀鎖假定當(dāng)前事務(wù)操縱數(shù)據(jù)資源時(shí),不會(huì)有其他事務(wù)同時(shí)訪問該數(shù)據(jù)資源,因此不作數(shù)據(jù)庫層次上的鎖定。為了維護(hù)正確的數(shù)據(jù),樂觀鎖使用應(yīng)用程序上的版本控制(由程序邏輯來實(shí)現(xiàn)的)來避免可能出現(xiàn)的并發(fā)問題。
唯一能夠同時(shí)保持高并發(fā)和高可伸縮性的方法就是使用帶版本化的樂觀并發(fā)控制。版本檢查使用版本號、 或者時(shí)間戳來檢測更新沖突(并且防止更新丟失)。
5.1.1. 使用版本檢查():
Hibernate中通過版本號檢查來實(shí)現(xiàn)后更新為主,這也是Hibernate推薦的方式。在數(shù)據(jù)庫表中加入一個(gè)version(版本)字段,在讀取數(shù)據(jù)時(shí)連同版本號一起讀取,并在更新數(shù)據(jù)時(shí)比較版本號與數(shù)據(jù)庫表中的版本號,如果等于數(shù)據(jù)庫表中的版本號則予以更新,并遞增版本號,如果小于數(shù)據(jù)庫表中的版本號就拋出異常。
使用進(jìn)行版本控制的步驟:
1) 在持久化類中定義一個(gè)代表版本號的屬性:
package org.qiujy.domain.versionchecking;
import java.util.Date;
public class Product implements java.io.Serializable{
private Long id ;
/** 版本號 */
private int version;
private String name; //產(chǎn)品名
private String description; //描述--簡介
private Double unitCost; //單價(jià)
private Date pubTime; //生產(chǎn)日期
public Product(){}
//以下為getter()和setter()方法
}
2) 在Product.hbm.xml文件中用元素來建立Product類的version屬性與表中version字段的映射。
3) Hibernate在其數(shù)據(jù)庫訪問引擎中內(nèi)置了樂觀鎖定實(shí)現(xiàn),默認(rèn)也是選擇version方式作為Hibernate樂觀鎖定實(shí)現(xiàn)機(jī)制。所以,在配置文件及程序中可以不作其它設(shè)置。按往常一樣寫操作代碼。
package org.qiujy.domain.versionchecking;
import java.util.Date;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.qiujy.common.HibernateSessionFactory;
public class TestVersionChecking {
public static void main(String[] args) {
Product prod = new Product();
prod.setName("IBM thinkPad T60");
prod.setUnitCost(new Double(26000.00));
prod.setDescription("筆記本電腦");
prod.setPubTime(new Date());
//test start.......
Session session = HibernateSessionFactory.getSession();
Transaction tx =null;
try{
tx = session.beginTransaction();
session.save(prod);
tx.commit();
}catch(HibernateException e){
if(tx != null){
tx.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
//進(jìn)行更新 測試..
prod.setDescription("新款的");
Session session2 = HibernateSessionFactory.getSession();
Transaction tx2 =null;
try{
tx2 = session2.beginTransaction();
session2.update(prod);
tx2.commit();
}catch(HibernateException e){
if(tx2 != null){
tx2.rollback();
}
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
}
}
新增數(shù)據(jù)時(shí)產(chǎn)生的SQL是:
insert into products (version, name, description, unitCost, pubTime)
values(?, ?, ?, ?, ?)
程序無需為Product對象的version屬性顯示賦值,當(dāng)持久化一個(gè)Product對象時(shí),Hibernate會(huì)自動(dòng)為它賦初始值為0。
更新數(shù)據(jù)時(shí)產(chǎn)生的SQL是:
update
products
set
version=?,
name=?,
description=?,
unitCost=?,
pubTime=?
where
id=?
and version=?
當(dāng)Hibernate更新一個(gè)Product對象,會(huì)根據(jù)它的id和version屬性到相應(yīng)的數(shù)據(jù)庫表中定位匹配的記錄,如果存在這條匹配的記錄,就更新記錄,并且把version字段的值加1。若找不到匹配的記錄,此時(shí)Hibernate會(huì)拋出StaleObjectStateException。
需要注意的是,由于樂觀鎖定是使用系統(tǒng)中的程序來控制,而不是使用數(shù)據(jù)庫中的鎖定機(jī)制,因而如果有人故意自行更新版本信息來超過檢查,則鎖定機(jī)制就無效。所以建議把持久化類中的version的get方法設(shè)置為private的。
5.1.2. 使用時(shí)間戳():
跟版本檢查的用法相似。不再多說。
5.2. 悲觀鎖(Pessimistic Locking):
悲觀鎖假定當(dāng)前事務(wù)操縱數(shù)據(jù)資源時(shí),肯定還會(huì)有其他事務(wù)同時(shí)訪問該數(shù)據(jù)資源,為了避免當(dāng)前事務(wù)的操作受到干擾,先鎖定資源。盡管悲觀鎖能夠防止丟失更新和不可重復(fù)讀這類并發(fā)問題,但是它影響并發(fā)性能,因此應(yīng)該很謹(jǐn)慎地使用悲觀鎖。
本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u2/65993/showart_2057120.html |
|