java事务的处理惩罚
添加时间:2013-7-19 点击量:
java的事务处理惩罚,若是对数据库进行多次操纵,每一次的履行或步调都是一个事务.若是数据库操纵在某一步没有履行或呈现异常而导致事务失败,如许有的事务被履行有的就没有被履行,从而就有了事务的回滚,作废先前的操纵.....
注:在Java中应用事务处理惩罚,起重请求数据库支撑事务。如应用MySQL的事务功能,就请求MySQL的表类型为Innodb才支撑事务。不然,在Java法度中做了commit或rollback,但在数据库中底子不克不及生效。
JavaBean中应用JDBC体式格式进行事务处理惩罚
public int (int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交体式格式 dbc.executeUpdate( xiao where ID= + sID); dbc.executeUpdate( xiao_content where ID= + sID); dbc.executeUpdate( xiao_affix where bylawid= + sID); con.commit();//提交JDBC事务 con.setAutoCommit(true);// 恢复JDBC事务的默认提交体式格式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
在数据库操纵中,一项事务是指由一条或多条对数据库更新的sql语句所构成的一个不成分别的工作单位。只有当事务中的所有操纵都正常完成了,全部事务才干被提交到数据库,若是有一项操纵没有完成,就必须作废全部事务。
例如在银行的转帐事务中,假定张三从本身的帐号上把1000元转到李四的帐号上,相干的sql语句如下:
account set monery=monery-1000 where name=zhangsan
account set monery=monery+1000 where name=lisi
这个两条语句必须作为一个完成的事务来处理惩罚。只有当两条都成功履行了,才干提交这个事务。若是有一句失败,全部事务必须作废。
在connection类中供给了3个把握事务的办法:
(1) setAutoCommit(Boolean autoCommit):设置是否主动提交事务;
(2) commit();提交事务;
(3) rollback();作废事务;
在jdbc api中,默认的景象为主动提交事务,也就是说,每一条对数据库的更新的sql语句代表一项事务,操纵成功后,体系主动调用commit()来提交,不然将调用rollback()来作废事务。
在jdbc api中,可以经由过程调用setAutoCommit(false) 来禁止主动提交事务。然后就可以把多条更新数据库的sql语句做为一个事务,在所有操纵完成之后,调用commit()来进行整体提交。假如此中一项 sql操纵失败,就不会履行commit()办法,而是产生响应的sqlexception,此时就可以捕获异常代码块中调用rollback()办法作废事务。
事务处理惩罚是企业应用须要解决的最首要的题目之一。J2EE经由过程JTA供给了完全的事务经管才能,包含多个事务性资料的经管才能。然则大项目组应用都是运行在单一的事务性资料之上(一个数据库),他们并不须要全局性的事务办事。本地事务办事已然足够(比如JDBC事务经管)。 本文并不评论辩论应当采取何种事务处理惩罚体式格式,首要目标是评论辩论如何更为优雅地设计事务办事。仅以JDBC事务处理惩罚为例。涉及到的DAO,Factory,Proxy,Decorator等模式概念,请浏览相干材料。 也许你据说过,事务处理惩罚应当做在service层,也许你也正如许做,然则否知道为什么如许做?为什么不放在DAO层干事务处理惩罚。显而易见的原因是营业层 接口的每一个办法有时辰都是一个营业用例(User Case),它须要调用不合的DAO对象来完成一个营业办法。比如简单地以网上书店购书最后的断定定单为例,营业办法起首是调用BookDAO对象(一般 是经由过程DAO工厂产生),BookDAO断定是否还有库存余量,取得该书的价格信息等,然后调用 CustomerDAO从帐户扣除响应的费用以及记录信息,然后是其他办事(通知经管员等)。简化营业流程可能如此: 重视,我们的例子忽视了连接的处理惩罚,只要包管同一个线程内取的是雷同的连接即可(可用ThreadLocal实现):
起首是营业接口,针对接口,而不是针对类编程:
public interface BookStoreManager{ public boolean buyBook(String bookId,int quantity)throws SystemException; ....其他营业办法 }
接下来就是营业接口的实现类??营业对象:
public class BookStoreManagerImpl implements BookStoreManager{ public boolean buyBook(String bookId)throws SystemException{ Connection conn=ConnectionManager.getConnection();//获取数据库连接 boolean b=false; try{ conn.setAutoCommit(false); //作废主动提交 BookDAO bookDAO=DAOFactory.getBookDAO(); CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); //测验测验从库存中取书 if(BookDAO.reduceInventory(conn,bookId,quantity)){ BigDecimal price=BookDAO.getPrice(bookId); //取价格 //从客户帐户中扣除pricequantity的费用 b= CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); .... 其他营业办法,如通知经管员,生成定单等. ... conn.commit(); //提交事务 conn.setAutoCommit(true); } }catch(SQLException e){ conn.rollback(); //呈现异常,回滚事务 con.setAutoCommit(true); e.printStackTrace(); throws new SystemException(e); } return b; } }
然后是营业代表工厂:
public final class ManagerFactory { public static BookStoreManager getBookStoreManager() { return new BookStoreManagerImpl(); } }
如许的设计很是合适于DAO中的简单活动,我们项目中的一个小体系也是采取如许的设计规划,然则它不合适于更大范围的应用。起首,你有没有闻到代码反复的 bad smell?每次都要设置AutoCommit为false,然后提交,呈现异常回滚,包装异常抛到上层,写多了不烦才怪,那能不克不及打消呢?其次,营业代 表对象如今知道它内部事务经管的所有的细节,这与我们设计营业代表对象的初志不符。对于营业代表对象来说,懂得一个与事务有关的营业束缚是相当恰当的,但 是让它负责来实现它们就不太恰当了。再次,你是否想过嵌套营业对象的场景?营业代表对象之间的互相调用,层层嵌套,此时你又如何处理惩罚呢?你要知道按我们现 在的体式格式,每个营业办法都处于各自自力的事务高低文傍边(Transaction Context),互相调用形成了嵌套事务,此时你又该如何处理惩罚?也许办法就是从头写一遍,把不合的营业办法集中成一个巨无霸包装在一个事务高低文中。
我们有更为优雅的设计来解决这类题目,若是我们把Transaction Context的把握交给一个被营业代表对象、DAO和其他Component所共知的外部对象。当营业代表对象的某个办法须要事务经管时,它提示此外部 对象它开端一个事务,外部对象获取一个连接并且开端数据库事务。也就是将事务把握从service层抽离,当 web层调用service层的某个营业代表对象时,返回的是一个经过Transaction Context外部对象包装(或者说)的营业对象。此对象将恳求发送给原始营业代表对象,然则对此中的营业办法进行事务把握。那么,我们如何实现 此结果呢?答案是JDK1.3引进的技巧。技巧只能接口,这也是为什么我们须要营业接口BookStoreManager的原因。 起首,我们引入这个Transaction Context外部对象,它的代码其实很简单,若是不懂得技巧的请先浏览其他材料。 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
import java.sql.Connection;
import com.strutslet.demo.service.SystemException;
public final class TransactionWrapper {
/ 装潢原始的营业代表对象,返回一个与营业代表对象有雷同接口的对象 / public static Object decorate(Object delegate) { return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), new XAWrapperHandler( delegate)); } //技巧 static final class XAWrapperHandler implements InvocationHandler { private final Object delegate;
XAWrapperHandler(Object delegate) { this.delegate = delegate; } //简单起见,包装营业代表对象所有的营业办法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; Connection con = ConnectionManager.getConnection(); try { //开端一个事务 con.setAutoCommit(false); //调用原始营业对象的营业办法 result = method.invoke(delegate, args); con.commit(); //提交事务 con.setAutoCommit(true); } catch (Throwable t) { //回滚 con.rollback(); con.setAutoCommit(true); throw new SystemException(t); }
return result; } } }
正如我们所见,此对象只不过把营业对象须要事务把握的营业办法中的事务把握项目组抽取出来罢了。请重视,营业代表对象内部调用自身的办法将不会开端新的事务,因为这些调用不会传给对象。如此,我们去除了代表反复的味道。此时,我们的营业代表对象批改成:
public class BookStoreManagerImpl implements BookStoreManager { public boolean buyBook(String bookId)throws SystemException{ Connection conn=ConnectionManager.getConnection();// 获取数据库连接 boolean b=false; try{ BookDAO bookDAO=DAOFactory.getBookDAO(); CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); // 测验测验从库存中取书 if(BookDAO.reduceInventory(conn,bookId,quantity)){ BigDecimal price=BookDAO.getPrice(bookId); // 取价格 // 从客户帐户中扣除pricequantity的费用 b= CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); .... 其他营业办法,如通知经管员,生成定单等. ... } }catch(SQLException e){ throws new SystemException(e); } return b; } .... }
可以看到,此时的营业代表对象专注于实现营业逻辑,它不再关怀事务把握细节,把它们全部委托给了外部对象。营业代表工厂也批改一下,让它返回两种类型的营业代表对象:
public final class ManagerFactory { //返回一个被包装的对象,有事务把握才能 public static BookStoreManager getBookStoreManagerTrans() { return (BookStoreManager) TransactionWrapper .decorate(new BookStoreManagerImpl()); } //原始版本 public static BookStoreManager getBookStoreManager() { return new BookStoreManagerImpl(); } ...... }
我们在营业代表工厂上供给了两种不合的对象生成办法:一个用于创建被包装的对象,它会为每次办法调用创建一个新的事务;别的一个用于创建未被包装的版本,它用于参加到已有的事务(比如其他营业代表对象的营业办法),解决了嵌套营业代表对象的题目。 我们的设计还不敷优雅,比如我们默认所有的营业代表对象的办法调用都将被包装在一个Transaction Context。可事实是很多办法也许并不须要与数据库打交道,若是我们能设备哪些办法须要事务声明,哪些不须要事务经管就更完美了。解决办法也很简单, 一个XML设备文件来设备这些,调用时断定即可。说到这里,懂得spring的可能都邑意识到这不恰是声明式事务把握吗?恰是如此,事务把握就是AOP的 一种办事,spring的声明式事务经管是经由过程AOP实现的。AOP的实现体式格式包含:技巧,字节码生成技巧(如CGLIB库),java代码生成 (早期EJB采取),批改类装载器以及源代码级此外代码混淆织入(aspectj)等。我们这里就是哄骗了技巧,只能对接口;对类的 可以应用cglib库
简单事务的概念
我不想从道理上申明什么是事务,应为那太呆板了。我只想从一个简单的例子来申明什么是事务。
例如我们有一个订单库存经管体系,每一次生成订单的同时我们都要消减库存。凡是来说订单和库存在数据库里是分两张表来保存的:订单表,库存表。每一次我们追加一个订单实际上须要两步操纵:在订单表中插入一条数据,同时批改库存的数据。
如许题目来了,例如我们须要一个单位为10的订单,库存中有30件,幻想的操纵是我们在订单表中插入了一条单位为10的订单,之后将库存表中的数据修 改为20。然则有些时辰工作并不是老是遵守你的设法产生,例如:在你批改库存的时辰,数据库忽然因为莫名其妙的原因无法连接上了。也就是说库存更新失败了。然则订单已经产生了,那么怎么办呢?没办法,只有手动的批改。所以好的体式格式是将订单插入的操纵和库存批改的操纵绑定在一路,必须同时成功或者什么都 不做。这就是事务。
Java如何处理惩罚事务呢?
我们从java.sql.Connection说起,Connection默示了一个和数据库的链接,可以经由过程Connection来对数据库操纵。 在凡是景象是Connection的属性是主动提交的,也就是说每次的操纵真的更新了数据库,真的无法回退了。针对上述的例子,一旦库存更新失败了,订单无法回退,因为订单真的插入到了数据库中。这并不是我们的。
我们的是:看起来成功了,然则没有真的操纵数据库,知道我想让他真的产生。可以经由过程Connection的setAutoCommit (false)让Connection不主动提交你的数据,除非你真的想提交。那么如何让操纵真的产生呢?可以应用Connection的commit方 法。如何让操纵回退呢?应用rollback办法。
例如:
try{
Connection conn = getConnection(); // 不管如何我们获得了链接
conn.setAutoCommit(false);
// 插入订单
// 批改库存
conn.commit(); // 成功的景象下,提交更新。
} catch(SQLException ex) {
conn.rollback(); // 失败的景象下,回滚所有的操纵
} finally {
conn.close();
}
这里有一点很是首要,事务是基于数据库链接的。所以在但数据库的景象下,事务操纵很简单。
那么若是表分布在两个不合的数据库中呢?
例如订单表在订单库中,库存表在库存库中,那么我们如何处理惩罚如许的事务呢?
须要重视,提交也可以碰到错误呀!
try{
Connection conn1 = getConnection1();
Connection conn2 = getConnection2();
// 基于conn1做插入操纵
// 基于conn2做更新操纵
try{
conn1.commit()
} catch(SQLExcetion ) {
conn1.rollback();
}
try {
conn2.commit();
} catch(SQLException ) {
conn2.rollbakc();
// 包管必然删除刚才插入的订单。
}
} catch(SQLException ex) {
// 若是插入失败,conn1.rollback
// 若是更新失败,conn1.rollback && conn2.rollback
} finally {
conn1.close();
conn2.close();
}
看看上述的代码就知道,其实操纵很是的错杂,甚至:包管必然删除刚才插入的订单底子无法包管。
在上述景象下的事务可以称之为分布式事务,经由过程上述的代码中事务同时提交处理惩罚的项目组我们可以得出,要想处理惩罚分布式事务,必须有自力于数据库的第三方的事务处理惩罚组件。
荣幸的是凡是景象下,JavaEE兼容的应用办事器,例如:Weblogic,Websphere,JBoss,Glassfish等都有这种分布式事务处理惩罚的组件。
如何应用应用办事器的分布式事务经管器处理惩罚分布式事务?
以galssfish为例
1 建树对应两个数据库的XA(javax.sql.XADataSource)类型的数据源。
2 应用UserTransaction来包管分布式事务。
try{
Connection conn1 = datasource1.getConnection();
Connection conn2 = datasource2.getConnection();
UserTransaction ut = getUserTransaction();
ut.begin();
// 插入订单
// 批改库存
ut.commit(); // 成功的景象下,提交更新。
} catch(SQLException ex) {
ut.rollback(); // 失败的景象下,回滚所有的操纵
} finally {
conn.close();
}
如何获取UserTransaction呢?可以应用如下办法
UserTransaction tx = (UserTransaction) ctx.lookup(jndi/UserTransaction);
J2EE开辟人员应用数据接见对象(DAO)设计模式把底层的数据接见逻辑和高层的商务逻辑分隔。实现DAO模式可以或许加倍专注于编写数据接见代码。这篇文章中,Java开辟人员Sean C. Sullivan从三个方面评论辩论DAO编程的布局特点:事务划分,异常处理惩罚,日记记录。
在比来的18个月,我和一个优良的软件开辟团队一路工作,开辟定制基于WEB的供给链经管应用法度.我们的应用法度接见广泛的持久层数据,包含出货状况,供给链轨制,库存,货色发运,项目经管数据,和用户属性等.我们应用JDBC API连接我们公司的各类数据库平台,并且在全部应用法度中应用了DAO设计模式.
经由过程在全部应用法度中应用数据接见对象(DAO)设计模式使我们可以或许把底层的数据接见逻辑和上层的商务逻辑分隔.我们为每个数据源创建了供给CRUD(创建,读取,更新,删除)操纵的DAO类.
在本文中,我将向你介绍DAO的实现策略以及创建更好的DAO类的技巧.我会明白的介绍日记记录,异常处理惩罚,和事务划分三项技巧.你将学在你的DAO类中如何把这三种技巧连络在一路.这篇文章假设你熟悉JDBC API,SQL和关系性数据库编程.
我们先往返顾一下DAO设计模式和数据接见对象.
DAO根蒂根基
DAO模式是标准的J2EE设计模式之一.开辟人员应用这个模式把底层的数据接见操纵和上层的商务逻辑分隔.一个典范的DAO实现有下列几个组件:
1. 一个DAO工厂类;
2. 一个DAO接口;
3. 一个实现DAO接口的具体类;
4. 数据传递对象(有些时辰叫做值对象).
具体的DAO类包含了从特定的数据源接见数据的逻辑。鄙人面的这段中你将学到设计和实现数据接见对象的技巧。
事务划分:
关于DAO要记住的一件首要工作是它们是事务性对象。每个被DAO履行的操纵(象创建,更新、或删除数据)都是和事务相接洽关系的。同样的,事务划分(transaction demarcation)的概念是希罕首要的。
事务划分是在事务界定定义中的体式格式。J2EE规范为事务划分描述了两种模式:编程务(programmatic)和声明务(declarative)。下表是对这两种模式的拆分:
声明务划分
编程务划分
法度员应用EJB的布署描述符声明事务属性
法度员担当编写事务逻辑代码的义务。
运行时景象(EJB容器)应用这些属性来主动的经管事务。
应用法度经由过程一个API接口来把握事务。
我将把重视力集中的编程务划分上。
象前面的介绍一样,DAOs是一些事务对象。一个典范的DAO要履行象创建、更新、和删除这的事务性操纵。在设计一个DAO时,起首要问本身如下题目:
1、 事务将如何开端?
2、 事务将如何停止?
3、 那个对象将承担起动一个事务的义务?
4、 那个对象将承担停止一个事务的义务?
5、 DAO应当承担起动和停止事务的义务?
6、 应用法度须要交叉接见多个DAO吗?
7、 一个事务包含一个DAO还是多个DAO?
8、 一个DAO包含其它的DAO中的办法吗?
答复这些题目将有助于你为DAO对象选择好的事务划分策略。对ADO中的事务划分有两个首要的策略。一种办法是应用DAO承担事务划分的义务;另一种是延期务,它把事务划分到调用DAO对象的办法中。若是你选择前者,你将要在DAO类中嵌入事务代码。若是你选择后者,事务代码将被写在DAO类的 外部。我们将应用简单的代码实例来更好的懂得这两种办法是如何工作的。
实例1显现了一个带有两种数据操纵的DAO:创建(create)和更新():
public void createWarehouseProfile(WHProfile profile); public void WarehouseStatus(WHIdentifier id, StatusInfo status);
实例2显现了一个简单的事务,事务划分代码是在DAO类的外部。重视:在这个例子中的调用者把多个DOA操纵组合到这个事务中。
tx.begin(); // start the transaction dao.createWarehouseProfile(profile); dao.WarehouseStatus(id1, status1); dao.WarehouseStatus(id2, status2); tx.commit(); // end the transaction
这种事务事务划分策略对在一个单一事务中接见多个DAO的应用法度来说尤为首要。
你即可应用JDBC API也可以应用Java 事务API(JTA)来实现事务的划分。JDBC事务划分比JTA事务划分简单,然则JTA供给了更好的灵活性。鄙人面的这段中,我们会进一步的看事务划分机制。
应用JDBC的事务划分
JDBC事务是应用Connection对象来把握的。JDBC的毗邻接口(java.sql.Connection)供给了两种事务模式:主动提交和手动提交。Java.sql.Connection为把握事务供给了下列办法:
.public void setAutoCommit(Boolean) .public Boolean getAutoCommit() .public void commit() .public void rollback()
实例3显现如何应用JDBC API来划分事务:
import java.sql.; import javax.sql.; // ... DataSource ds = obtainDataSource(); Connection conn = ds.getConnection(); conn.setAutoCommit(false); // ... pstmt = conn.prepareStatement(;UPDATE MOVIES ...;); pstmt.setString(1, ;The Great Escape;); pstmt.executeUpdate(); // ... conn.commit(); // ...
应用JDBC事务划分,你可以或许把多个SQL语句组合到一个单一事务中。JDBC事务的毛病之一就是事务局限被限制在一个单一的数据库连接中。一个 JDBC事务不克不及够跨越多个数据库。接下来,我们会看到如何应用JTA来干事务划分的。因为JTA不象JDBC那样被广泛的懂得,所以我起首概要的介绍一 下JTA。
JTA概要介绍
Java事务API(JTA;Java Transaction API)和它的同胞Java事务办事(JTS;Java Transaction Service),为J2EE平台供给了分布式事务办事。一个分布式事务(distributed transaction)包含一个事务经管器(transaction manager)和一个或多个资料经管器(resource manager)。一个资料经管器(resource manager)是随便率性类型的持久化数据存储。事务经管器(transaction manager)承担着所有事务参与单位者的彼此通信的义务。下车站显示了事务经管器和资料经管的间的关系。
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限制在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:
.JDBC连接
.JDO PersistenceManager 对象
.JMS 队列
.JMS 主题
.企业JavaBeans(EJB)
.一个用J2EE Connector Architecture 规范编译的资料分派器。
应用JTA的事务划分
要用JTA来划分一个事务,应用法度调用javax.transaction.UserTransaction接口中的办法。示例4显示了一个典范的JNDI搜刮的UseTransaction对象。
import javax.transaction.; import javax.naming.; // ... InitialContext ctx = new InitialContext(); Object txObj = ctx.lookup(;java:comp/UserTransaction;); UserTransaction utx = (UserTransaction) txObj;
应用法度有了UserTransaction对象的引用之后,就可以象示例5那样来起动事务。
utx.begin(); // ... DataSource ds = obtainXADataSource(); Connection conn = ds.getConnection(); pstmt = conn.prepareStatement(;UPDATE MOVIES ...;); pstmt.setString(1, ;Spinal Tap;); pstmt.executeUpdate(); // ... utx.commit(); // ...
当应用法度调用commit()时,事务经管器应用两段提交和谈来停止事务。JTA事务把握的办法:
.javax.transaction.UserTransaction接口供给了下列事务把握办法:
.public void begin() .public void commit() .public void rollback() .public void getStatus() .public void setRollbackOnly() .public void setTransactionTimeout(int)
应用法度调用begin()来起动事务,即可调用commit()也可以调用rollback()来停止事务。
应用JTA和JDBC
开辟人员经常应用JDBC来作为DAO类中的底层数据操纵。若是规划应用JTA来划分事务,你将须要一个实现了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驱动。实现了这些接口的驱动将有才能参与到JTA事务中。一个XADataSource对象是一个XAConnection对象的工厂。XAConnections是参与到JTA事务中的连接。
你须要应用应用法度办事器经管对象来建树XADataSource对象。对于特别的指令请参考应用法度办事器文档和JDBC驱动文档。
J2EE应用法度应用JNDI来查找数据源。一旦应用法度有了一个数据源对象的引用,这会调用javax.sql.DataSource.getConnection()来获得数据库的连接。
XA连接差别于非XA连接。要记住的是XA连接是一个JTA事务中的参与者。这就意味着XA连接不支撑JDBC的主动提交特点。也就是说应用法度不必 在XA连接上调用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,应 用法度应当应用UserTransaction.begin()、UserTransaction.commit()和 UserTransaction.rollback().
选择好的办法
我们已经评论辩论了JDBC和JTA是如何划分事务的。每一种办法都有它的长处,回此你须要决意为你的应用法度选择一个最适应的办法。在我们团队很多比来的对于事务划分的项目中应用JDBC API来创建DAO类。这DAO类总结如下:
.事务划分代码被嵌入到DAO类内部
.DAO类应用JDBC API来进行事务划分
.调用者没有划分事务的办法
.事务局限被限制在一个单一的JDBC连接
JDBC事务对错杂的企业应用法度不老是有效的。若是你的事务将跨越多个DAO对象或多个数据库,那么下面的实现策略可能会更恰当:
.用JTA对事务进行划分
.事务划分代码被DAO分隔
.调用者承担划分事务的义务
.DAO参与一个全局的事务中
JDBC办法因为它的简略单纯性而具有吸引力,JTA办法供给了更多灵活性。你选择什么样的实现将依附于你的应用法度的特定需求。
日记记录和DAO
一个好的DAO实现类将应用日记记录来捕获有关它在运行时的行动细节。你可以选择记录异常、设备信息、连接状况、JDBC驱动法度的元数据或查询参数。日记对开辟全部阶段都是有益的。我经常搜检应用法度在开辟时代、测试时代和产品中的日记记录。
在这段中,我们将显现一段如何把Jakarta Commaons Logging连络中一个DAO中的例子。在我们开端之前,让我们先回顾一些根蒂根基常识。
选择一个日记例库
很多开辟人员应用的基本日记情势是:System.out.println和System.err.println.Println语句。这种情势快捷便利,但它们不克不及供给一个完全的日记体系的的才能。下表列出了Java平台的日记类库:
日记类库
开源吗?
URL
Java.util.logging
否
http://java.sun.com/j2ee
Jakarta Log4j
是
http://hajarta.apache.org/log4j/
Jakarta Commons Logging
是
http:/Jakarta.apache.org/commons/logging.html
Java.util.logging是J2SE1.4平台上的标准的API。然则,大多半开辟人员都认为Jakarta Log4j供给了更大的功能性和灵活性。Log4j超出java.util.logging的长处之一就是它支撑J2SE1.3和J2SE1.4平台。
Jakarta Commons Logging可以或许被用于和java.util.loggin或Jakarta Log4j一路工作。Commons Logging是一个把你的应用法度自力于日记实现的提取层。应用Commons Logging你可以或许经由过程改变一个设备文件来与下面的日记实现来互换数据。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一个日记示例
示例7显示了在一个DOA类中如何应用Jakarta Commons Logging
import org.apache.commons.logging.; class DocumentDAOImpl implements DocumentDAO { static private final Log log = LogFactory.getLog(DocumentDAOImpl.class); public void Document(String id) { // ... log.debug(;deleting document: ; + id); // ... try { // ... data operations ... } catch (SomeException ex) { log.error(;Unable to document; ex); // ... handle the exception ... } } }
日记是评估应用法度的根蒂根基项目组。若是你在一个DAO中碰到了失败,日记经常会为懂得产生的什么错误供给好的信息。把日记连络到你的DAO中,确保获得调试和解决题目的有效手段。
DAO中的异常处理惩罚
我们已经看了事务划分和日记记录,并且如今对于它们是如何应用于数据接见对象的有一个深切的懂得。我们第三项目组也是最后要评论辩论的是异常处理惩罚。下面的一些简单的异常处理惩罚方针应用你的DAO更轻易应用,加倍结实和更具有可保护性。
在实现DAO模式的时辰,要考滤下面的题目:
.在DAO的public接口中的办法将抛出被搜检的异常吗?
.若是是,将抛出什么样的搜检性异常?
.在DAO实现类中怎能样处理惩罚异常。
在用DAO模式工作的过程中,我们的团队为异常处理惩罚开辟了一组方针。下面的这些方针会很大程度的改良你的DAO:
.DAO办法应当抛出有意义的异常。
.DAO办法不该该抛出java.lang.Exception异常。因为java.lang.Exception太一般化,它不克不及包含有关潜伏题目的所有信息。
.DAO办法不该该抛出java.sql.SQLException异常。SQLException是一个底层的JDBC异常,DAO应用尽力封装JDBC异常而不该该把JDBC异常留给应用法度的其它项目组。
.在DAO接口中的办法应当只抛出调用者期望处理惩罚的搜检性异常。若是调用者不克不及用恰当的办法来处理惩罚异常,考滤抛出不搜检性(运行时run-time)异常。
.若是你的数据接见代码捕获了一个异常,不成要忽视它。忽视捕获异常的DAO是很处理惩罚的。
.应用异常链把底层的异常传递给高层的某个处理惩罚器。
.考滤定义一个标准的DAO异常类。Spring框架供给了一个优良的预定义的DAO异常类的凑集。
看Resources,查看有异常和异常处理惩罚技巧的更具体信息。
实现示例:MovieDAO
MoveDAO是一个示范了在这篇文章中所评论辩论的所有技巧,包含事务划分、日记记录和异常处理惩罚。你会在Resources段找到MovieDAO的源代码。它被分下面的三个包:
.daoexamples.exception .daoexamples.move .daoexamples.moviedemo
这个DAO模式的实现由下面的类和接口构成:
.daoexamples.movie.MovieDAOFactory .daoexamples.movie.MovieDAO .daoexamples.movie.MovieDAOImpl .daoexamples.movie.MovieDAOImplJTA .daoexamples.movie.Movie .daoexamples.movie.MovieImple .daoexamples.movie.MovieNotFoundException .daoexamples.movie.MovieUtil
MovieDAO接口定义了DAO的数据操纵。这个接口有如下五个办法:
.public Movie findMovieById(String id) .public java.util.Collection findMoviesByYear(String year) .public void Movie(String id) .public Movie createMovie(String rating,String year,String title) .public void Movie(String id,String rating,String year,String title)
daoexamples.movie包包含了两个MovieDAO接口的实现。每个实现应用了一个同的事务划分办法,如下表所示:
MovieDAOImpl
MovieDAOImplJTA
实现了MovieDAO接口吗?
Yes
Yes
经由过程JNDI获得DataSource吗?
Yes
Yes
从一个DataSource获得java.sql.Connection对象吗?
Yes
Yes
DAO界定内部的事务吗?
Yes
No
应用JDBC事务吗?
Yes
No
应用一个XA DataSource吗?
No
Yes
分担JTA事务吗?
No
Yes
MovieDAO 示范应用法度
这个示范应用法度是一个叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet类,它应用Movie DAO来查询和更新一个表中的movie数据。
这个servlet示范了把JTA感知的MovieDAO和Java消息办事组合到一个单一的事务中,如示例8所示:
UserTransaction utx = MovieUtil.getUserTransaction(); utx.begin(); batman = dao.createMovie(;R; ;2008; ;Batman Reloaded;); publisher = new MessagePublisher(); publisher.publishTextMessage(;I’ll be back;); dao.Movie(topgun.getId(), ;PG-13; topgun.getReleaseYear(), topgun.getTitle()); dao.Movie(legallyblonde.getId()); utx.commit();
要运行这个典范应用法度,在你的应用法度办事器中设备一个XA 数据源和一个非XA数据源。然后布署daoexamples.ear文件。这个应用法度将运行在任何与J2EE兼容的应用法度办事器。
事务处理惩罚
信息是任何企事业单位的首要资产,任何企业项目组都包含着信息的流入、流出,任何企业项目组都把握着某些信息。同时,信息必须在恰当的机会传播给须要的 人。并且,信息还须要安然束缚,凡是按照信息的类型和内容实验接见把握。为了包管数据的安然有效和正确靠得住,数据库经管体系(DBMS)必须供给同一的数 据保护功能。
事务是现代数据库理论中的核心概念之一。若是一组处理惩罚步调或者全部产生或者一步也不履行,我们称该组处理惩罚步调为一个事务。当所有的步调像一个操纵一样被完全地履行,我们称该事务被提交。因为此中的一项目组或多步履行失败,导致没有步调被提交,则事务必须回滚(回到最初的体系状况)。事务必须功用 ISO/IEC所制订的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久 性(durability)的缩写。事务的原子性默示事务履行过程中的任何失败都将导致事务所做的任何批改失效。一致性默示当事务履行失败时,所有被该事务影响的数据都应当恢复到事务履行前的状况。隔离性默示在事务履行过程中对数据的批改,在事务提交之前对其他事务不成见。持久性默示已提交的数据在事务执 行失败时,数据的状况都应当正确。
鄙人面我们列举一个应用SQL Server数据库进行事务处理惩罚的例子。主表是一个规章轨制信息表(bylaw),首要字段有记录编号、题目、作者、书写日期等。两个子表分别是附件表 (bylaw_affix)和文本信息表(bylaw_content)。表布局见图1所示。bylaw表的记录编号与bylaw_affix表的记录编 号、bylaw_content表的记录编号是对应的,每次对规章轨制信息的操纵也就是对这三个表的结合操纵。例如要删除规章轨制中的一笔记录,若是不应用事务,就可能会呈现如许的景象:第一个表中成功删除后,数据库忽然呈现不测状况,而第二、三个表中的操纵没有完成,如许,删除操纵并没有完成,甚至已经 破损数据库中的数据。要避免这种景象,就应当应用事务,它的感化是:要么三个表都操纵成功,要么都失败。换句话说,就是对峙数据的一致性。所以,为了确保对数据操纵的完全和一致,在法度设计时要充沛推敲到事务处理惩罚方面的题目。
图1 示例表布局
Java中的事务处理惩罚
一般景象下,J2EE应用办事器支撑JDBC事务、JTA(Java Transaction API)事务、容器经管事务。一般景象下,好不要在法度中同时应用上述三种事务类型,比如在JTA事务中嵌套JDBC事务。第二方面,事务要在尽可能短 的时候内完成,不要在不合办法中实现事务的应用。下面我们列举两种事务处理惩罚体式格式。
1、JavaBean中应用JDBC体式格式进行事务处理惩罚 在JDBC中如何将多个SQL语句组合成一个事务呢?在JDBC中,打开一个连接对象Connection时,缺省是auto-commit模式,每个 SQL语句都被算作一个事务,即每次履行一个语句,都邑主动的获得事务确认。为了能将多个SQL语句组合成一个事务,要将auto-commit模式樊篱 掉。在auto-commit模式樊篱掉之后,若是不调用commit()办法,SQL语句不会获得事务确认。在比来一次commit()办法调用之后的 所有SQL会在办法commit()调用时获得确认。
public int (int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交体式格式 dbc.executeUpdate( bylaw where ID= + sID); dbc.executeUpdate( bylaw _content where ID= + sID); dbc.executeUpdate( bylaw _affix where bylawid= + sID); con.commit();//提交JDBC事务 con.setAutoCommit(true);// 恢复JDBC事务的默认提交体式格式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
2、SessionBean中的JTA事务 JTA 是事务办事的 J2EE 解决规划。本质上,它是描述事务接口(比如 UserTransaction 接口,开辟人员直接应用该接口或者经由过程 J2EE 容器应用该接口来确保营业逻辑可以或许靠得住地运行)的 J2EE 模型的一项目组。JTA 具有的三个首要的接口分别是 UserTransaction 接口、TransactionManager 接口和 Transaction 接口。这些接口共享公共的事务操纵,例如 commit() 和 rollback(), 然则也包含特别的事务操纵,例如 suspend(),resume() 和 enlist(),它们只呈如今特定的接口上,以便在实现中容许必然程度的接见把握。例如,UserTransaction 可以或许履行事务划分和根蒂根基的事务操纵,而 TransactionManager 可以或许履行高低文经管。 应用法度可以调用UserTransaction.begin()办法开端一个事务,该事务与应用法度正在此中运行的当火线程相接洽关系。底层的事务经管器实际处理惩罚线程与事务之间的接洽关系。UserTransaction.commit()办法终止与当火线程接洽关系的事务。 UserTransaction.rollback()办法将放弃与当火线程接洽关系的当前事务。
public int (int sID) { DataBaseConnection dbc = null; dbc = new DataBaseConnection(); dbc.getConnection(); UserTransaction transaction = sessionContext.getUserTransaction();//获得JTA事务 try { transaction.begin(); //开端JTA事务 dbc.executeUpdate( bylaw where ID= + sID); dbc.executeUpdate( bylaw _content where ID= + sID); dbc.executeUpdate( bylaw _affix where bylawid= + sID); transaction.commit(); //提交JTA事务 dbc.close(); return 1; } catch (Exception exc) { try { transaction.rollback();//JTA事务回滚 } catch (Exception ex) { //JTA事务回滚失足处理惩罚 ex.printStackTrace(); } exc.printStackTrace(); dbc.close(); return -1; } }
Cant start a cloned connection while in manual transaction mode错误2008-03-13 20:30呈现Cant start a cloned connection while in manual transaction mode错误,从网上找到原因及解决办法如下: 原因一般是当你在一个SQL SERVER的JDBC连接上履行多个STATEMENTS的操纵,或者是手动事务状况(AutoCommit=false) 并且应用默认的模式. direct (SelectMethod=direct) 模式.
解决办法 当你应用手动事务模式时,必须把SelectMethod 属性的值设置为 Cursor, 或者是确保在你的连接只有一个STATEMENT操纵。
批改url
参加SelectMethod=cursor即可
如:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=ys;SelectMethod=Cursor;User=ys;Password=ys); package _class; import java.sql.; import java.util.StringTokenizer; public class connDB{ String sDBDriver = com.microsoft.jdbc.sqlserver.SQLServerDriver; String sConnStr = jdbc:microsoft:sqlserver://127.0.0.1:1433;SelectMethod=cursor;DatabaseName=myDB;user=sa;password=river; Connection cn = null; Statement stmt; boolean autoCommit; private String DbType=MYSQL; //private String DbType=Oracle; private connDB(){ init(); } private void init(){ try{ Class.forName(sDBDriver).newInstance(); cn = DriverManager.getConnection(sConnStr); }catch(Exception e){ System.err.println(conndb():连接异常. + e.getMessage()); } } public static connDB getNewInstance(){ return new connDB(); } //数据绑定的材料如同很少,有空给大师一个例子。在这里只能返回PreparedStatement。 public PreparedStatement getPreparedStmt(String sql) throws SQLException{ PreparedStatement preStmt=null; try{ preStmt = cn.prepareStatement(sql); }catch(SQLException ex){ ex.printStackTrace(); throw ex; } return preStmt; } public void beginTrans() throws SQLException{ try{ autoCommit=cn.getAutoCommit(); cn.setAutoCommit(false); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(beginTrans Errors); throw ex; } } public void commit()throws SQLException{ try{ cn.commit(); cn.setAutoCommit(autoCommit); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(Commit Errors); throw ex; } } public void rollback(){ try{ cn.rollback(); cn.setAutoCommit(autoCommit); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(Rollback Errors); //throw ex; } } public boolean getAutoCommit() throws SQLException{ boolean result=false; try{ result=cn.getAutoCommit(); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(getAutoCommit fail+ex.getMessage()); throw ex; } return result; } //默认的景象下一次executeQuery(String sql)是一次事务。 //然则可以调用beginTrans(),然后多次executeQuery(String sql),最后commit()实现多sql的事务处理惩罚(重视在这种景象下若是产生违例,切切不要忘了在catch(){调用rollBack()})。 // public ResultSet executeQuery(String sql) throws SQLException{ ResultSet rs = null; try{ stmt=cn.createStatement(); rs = stmt.executeQuery(sql); } catch(SQLException ex) { ex.printStackTrace(); System.out.println(conndb.executeQuery:+ex.getMessage()); throw ex; } return rs; } public void executeUpdate(String sql) throws SQLException{ try{ stmt=cn.createStatement(); stmt.executeUpdate(sql); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(conndb.executeUpdate:+ex.getMessage()); throw ex; } } //Method doBatch 的参数sql,是由一些sql语句拼起来的,用;隔开。可以将很多的sql放在一个事务中,一次履行。 public int[] doBatch(String sql) throws SQLException{ int[] rowResult=null; String a; try{ //boolean autoCommit=cn.getAutoCommit(); //cn.setAutoCommit(false); stmt=cn.createStatement(); StringTokenizer st = new StringTokenizer(sql,;); while (st.hasMoreTokens()){ a=st.nextToken(); stmt.addBatch(a); } rowResult=stmt.executeBatch(); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(conndb.doBatch:+ex.getMessage()); throw ex; } return rowResult; } public String getDbType(){ return DbType; } public void close() throws SQLException{ try{ stmt.close(); stmt=null; cn.close(); cn=null; }catch(SQLException ex){ ex.printStackTrace(); System.out.println(Closeing connection fail+ex.getMessage()); throw ex;
} } public static void main(String[] args)throws Exception{ connDB con=connDB.getNewInstance(); System.out.println(con.getDbType()); String sql2= into test values(0510321315,李白,80);; String s1= test; con.beginTrans(); ResultSet rs=con.executeQuery(s1); con.executeUpdate(sql2);System.out.println(con.executeUpdate(sql2);); /try{ int up=s.executeUpdate(sql2); if(up!=0)System.out.println(语句:+sql2+插入成功!); else System.out.println(语句:+sql2+插入失败!); }catch(SQLException e){System.out.println(e);}/ //ResultSet rs=s.executeQuery( titles); con.executeUpdate( test where sno=0510321315);System.out.println(con.executeUpdate(\ test where sno=0510321315\);); con.commit(); while(rs.next()){ System.out.print(rs.getString(1)+\t); System.out.print(rs.getString(2)+\t); System.out.print(rs.getInt(3)+\t); System.out.println( ); }
con.close(); } }
读书,不要想着实用,更不要有功利心。读书只为了自身的修养。邂逅一本好书如同邂逅一位知己,邂逅一个完美之人。有时心生敬意,有时怦然心动。仿佛你心底埋藏多年的话,作者替你说了出来,你们在时光深处倾心相遇的一瞬间,情投意合,心旷神怡。
java的事务处理惩罚,若是对数据库进行多次操纵,每一次的履行或步调都是一个事务.若是数据库操纵在某一步没有履行或呈现异常而导致事务失败,如许有的事务被履行有的就没有被履行,从而就有了事务的回滚,作废先前的操纵.....
注:在Java中应用事务处理惩罚,起重请求数据库支撑事务。如应用MySQL的事务功能,就请求MySQL的表类型为Innodb才支撑事务。不然,在Java法度中做了commit或rollback,但在数据库中底子不克不及生效。
JavaBean中应用JDBC体式格式进行事务处理惩罚
public int (int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交体式格式 dbc.executeUpdate( xiao where ID= + sID); dbc.executeUpdate( xiao_content where ID= + sID); dbc.executeUpdate( xiao_affix where bylawid= + sID); con.commit();//提交JDBC事务 con.setAutoCommit(true);// 恢复JDBC事务的默认提交体式格式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
在数据库操纵中,一项事务是指由一条或多条对数据库更新的sql语句所构成的一个不成分别的工作单位。只有当事务中的所有操纵都正常完成了,全部事务才干被提交到数据库,若是有一项操纵没有完成,就必须作废全部事务。
例如在银行的转帐事务中,假定张三从本身的帐号上把1000元转到李四的帐号上,相干的sql语句如下:
account set monery=monery-1000 where name=zhangsan
account set monery=monery+1000 where name=lisi
这个两条语句必须作为一个完成的事务来处理惩罚。只有当两条都成功履行了,才干提交这个事务。若是有一句失败,全部事务必须作废。
在connection类中供给了3个把握事务的办法:
(1) setAutoCommit(Boolean autoCommit):设置是否主动提交事务;
(2) commit();提交事务;
(3) rollback();作废事务;
在jdbc api中,默认的景象为主动提交事务,也就是说,每一条对数据库的更新的sql语句代表一项事务,操纵成功后,体系主动调用commit()来提交,不然将调用rollback()来作废事务。
在jdbc api中,可以经由过程调用setAutoCommit(false) 来禁止主动提交事务。然后就可以把多条更新数据库的sql语句做为一个事务,在所有操纵完成之后,调用commit()来进行整体提交。假如此中一项 sql操纵失败,就不会履行commit()办法,而是产生响应的sqlexception,此时就可以捕获异常代码块中调用rollback()办法作废事务。
事务处理惩罚是企业应用须要解决的最首要的题目之一。J2EE经由过程JTA供给了完全的事务经管才能,包含多个事务性资料的经管才能。然则大项目组应用都是运行在单一的事务性资料之上(一个数据库),他们并不须要全局性的事务办事。本地事务办事已然足够(比如JDBC事务经管)。 本文并不评论辩论应当采取何种事务处理惩罚体式格式,首要目标是评论辩论如何更为优雅地设计事务办事。仅以JDBC事务处理惩罚为例。涉及到的DAO,Factory,Proxy,Decorator等模式概念,请浏览相干材料。 也许你据说过,事务处理惩罚应当做在service层,也许你也正如许做,然则否知道为什么如许做?为什么不放在DAO层干事务处理惩罚。显而易见的原因是营业层 接口的每一个办法有时辰都是一个营业用例(User Case),它须要调用不合的DAO对象来完成一个营业办法。比如简单地以网上书店购书最后的断定定单为例,营业办法起首是调用BookDAO对象(一般 是经由过程DAO工厂产生),BookDAO断定是否还有库存余量,取得该书的价格信息等,然后调用 CustomerDAO从帐户扣除响应的费用以及记录信息,然后是其他办事(通知经管员等)。简化营业流程可能如此: 重视,我们的例子忽视了连接的处理惩罚,只要包管同一个线程内取的是雷同的连接即可(可用ThreadLocal实现):
起首是营业接口,针对接口,而不是针对类编程:
public interface BookStoreManager{ public boolean buyBook(String bookId,int quantity)throws SystemException; ....其他营业办法 }
接下来就是营业接口的实现类??营业对象:
public class BookStoreManagerImpl implements BookStoreManager{ public boolean buyBook(String bookId)throws SystemException{ Connection conn=ConnectionManager.getConnection();//获取数据库连接 boolean b=false; try{ conn.setAutoCommit(false); //作废主动提交 BookDAO bookDAO=DAOFactory.getBookDAO(); CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); //测验测验从库存中取书 if(BookDAO.reduceInventory(conn,bookId,quantity)){ BigDecimal price=BookDAO.getPrice(bookId); //取价格 //从客户帐户中扣除pricequantity的费用 b= CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); .... 其他营业办法,如通知经管员,生成定单等. ... conn.commit(); //提交事务 conn.setAutoCommit(true); } }catch(SQLException e){ conn.rollback(); //呈现异常,回滚事务 con.setAutoCommit(true); e.printStackTrace(); throws new SystemException(e); } return b; } }
然后是营业代表工厂:
public final class ManagerFactory { public static BookStoreManager getBookStoreManager() { return new BookStoreManagerImpl(); } }
如许的设计很是合适于DAO中的简单活动,我们项目中的一个小体系也是采取如许的设计规划,然则它不合适于更大范围的应用。起首,你有没有闻到代码反复的 bad smell?每次都要设置AutoCommit为false,然后提交,呈现异常回滚,包装异常抛到上层,写多了不烦才怪,那能不克不及打消呢?其次,营业代 表对象如今知道它内部事务经管的所有的细节,这与我们设计营业代表对象的初志不符。对于营业代表对象来说,懂得一个与事务有关的营业束缚是相当恰当的,但 是让它负责来实现它们就不太恰当了。再次,你是否想过嵌套营业对象的场景?营业代表对象之间的互相调用,层层嵌套,此时你又如何处理惩罚呢?你要知道按我们现 在的体式格式,每个营业办法都处于各自自力的事务高低文傍边(Transaction Context),互相调用形成了嵌套事务,此时你又该如何处理惩罚?也许办法就是从头写一遍,把不合的营业办法集中成一个巨无霸包装在一个事务高低文中。
我们有更为优雅的设计来解决这类题目,若是我们把Transaction Context的把握交给一个被营业代表对象、DAO和其他Component所共知的外部对象。当营业代表对象的某个办法须要事务经管时,它提示此外部 对象它开端一个事务,外部对象获取一个连接并且开端数据库事务。也就是将事务把握从service层抽离,当 web层调用service层的某个营业代表对象时,返回的是一个经过Transaction Context外部对象包装(或者说)的营业对象。此对象将恳求发送给原始营业代表对象,然则对此中的营业办法进行事务把握。那么,我们如何实现 此结果呢?答案是JDK1.3引进的技巧。技巧只能接口,这也是为什么我们须要营业接口BookStoreManager的原因。 起首,我们引入这个Transaction Context外部对象,它的代码其实很简单,若是不懂得技巧的请先浏览其他材料。 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
import java.sql.Connection;
import com.strutslet.demo.service.SystemException;
public final class TransactionWrapper {
/ 装潢原始的营业代表对象,返回一个与营业代表对象有雷同接口的对象 / public static Object decorate(Object delegate) { return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), new XAWrapperHandler( delegate)); } //技巧 static final class XAWrapperHandler implements InvocationHandler { private final Object delegate;
XAWrapperHandler(Object delegate) { this.delegate = delegate; } //简单起见,包装营业代表对象所有的营业办法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; Connection con = ConnectionManager.getConnection(); try { //开端一个事务 con.setAutoCommit(false); //调用原始营业对象的营业办法 result = method.invoke(delegate, args); con.commit(); //提交事务 con.setAutoCommit(true); } catch (Throwable t) { //回滚 con.rollback(); con.setAutoCommit(true); throw new SystemException(t); }
return result; } } }
正如我们所见,此对象只不过把营业对象须要事务把握的营业办法中的事务把握项目组抽取出来罢了。请重视,营业代表对象内部调用自身的办法将不会开端新的事务,因为这些调用不会传给对象。如此,我们去除了代表反复的味道。此时,我们的营业代表对象批改成:
public class BookStoreManagerImpl implements BookStoreManager { public boolean buyBook(String bookId)throws SystemException{ Connection conn=ConnectionManager.getConnection();// 获取数据库连接 boolean b=false; try{ BookDAO bookDAO=DAOFactory.getBookDAO(); CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); // 测验测验从库存中取书 if(BookDAO.reduceInventory(conn,bookId,quantity)){ BigDecimal price=BookDAO.getPrice(bookId); // 取价格 // 从客户帐户中扣除pricequantity的费用 b= CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); .... 其他营业办法,如通知经管员,生成定单等. ... } }catch(SQLException e){ throws new SystemException(e); } return b; } .... }
可以看到,此时的营业代表对象专注于实现营业逻辑,它不再关怀事务把握细节,把它们全部委托给了外部对象。营业代表工厂也批改一下,让它返回两种类型的营业代表对象:
public final class ManagerFactory { //返回一个被包装的对象,有事务把握才能 public static BookStoreManager getBookStoreManagerTrans() { return (BookStoreManager) TransactionWrapper .decorate(new BookStoreManagerImpl()); } //原始版本 public static BookStoreManager getBookStoreManager() { return new BookStoreManagerImpl(); } ...... }
我们在营业代表工厂上供给了两种不合的对象生成办法:一个用于创建被包装的对象,它会为每次办法调用创建一个新的事务;别的一个用于创建未被包装的版本,它用于参加到已有的事务(比如其他营业代表对象的营业办法),解决了嵌套营业代表对象的题目。 我们的设计还不敷优雅,比如我们默认所有的营业代表对象的办法调用都将被包装在一个Transaction Context。可事实是很多办法也许并不须要与数据库打交道,若是我们能设备哪些办法须要事务声明,哪些不须要事务经管就更完美了。解决办法也很简单, 一个XML设备文件来设备这些,调用时断定即可。说到这里,懂得spring的可能都邑意识到这不恰是声明式事务把握吗?恰是如此,事务把握就是AOP的 一种办事,spring的声明式事务经管是经由过程AOP实现的。AOP的实现体式格式包含:技巧,字节码生成技巧(如CGLIB库),java代码生成 (早期EJB采取),批改类装载器以及源代码级此外代码混淆织入(aspectj)等。我们这里就是哄骗了技巧,只能对接口;对类的 可以应用cglib库
简单事务的概念
我不想从道理上申明什么是事务,应为那太呆板了。我只想从一个简单的例子来申明什么是事务。
例如我们有一个订单库存经管体系,每一次生成订单的同时我们都要消减库存。凡是来说订单和库存在数据库里是分两张表来保存的:订单表,库存表。每一次我们追加一个订单实际上须要两步操纵:在订单表中插入一条数据,同时批改库存的数据。
如许题目来了,例如我们须要一个单位为10的订单,库存中有30件,幻想的操纵是我们在订单表中插入了一条单位为10的订单,之后将库存表中的数据修 改为20。然则有些时辰工作并不是老是遵守你的设法产生,例如:在你批改库存的时辰,数据库忽然因为莫名其妙的原因无法连接上了。也就是说库存更新失败了。然则订单已经产生了,那么怎么办呢?没办法,只有手动的批改。所以好的体式格式是将订单插入的操纵和库存批改的操纵绑定在一路,必须同时成功或者什么都 不做。这就是事务。
Java如何处理惩罚事务呢?
我们从java.sql.Connection说起,Connection默示了一个和数据库的链接,可以经由过程Connection来对数据库操纵。 在凡是景象是Connection的属性是主动提交的,也就是说每次的操纵真的更新了数据库,真的无法回退了。针对上述的例子,一旦库存更新失败了,订单无法回退,因为订单真的插入到了数据库中。这并不是我们的。
我们的是:看起来成功了,然则没有真的操纵数据库,知道我想让他真的产生。可以经由过程Connection的setAutoCommit (false)让Connection不主动提交你的数据,除非你真的想提交。那么如何让操纵真的产生呢?可以应用Connection的commit方 法。如何让操纵回退呢?应用rollback办法。
例如:
try{
Connection conn = getConnection(); // 不管如何我们获得了链接
conn.setAutoCommit(false);
// 插入订单
// 批改库存
conn.commit(); // 成功的景象下,提交更新。
} catch(SQLException ex) {
conn.rollback(); // 失败的景象下,回滚所有的操纵
} finally {
conn.close();
}
这里有一点很是首要,事务是基于数据库链接的。所以在但数据库的景象下,事务操纵很简单。
那么若是表分布在两个不合的数据库中呢?
例如订单表在订单库中,库存表在库存库中,那么我们如何处理惩罚如许的事务呢?
须要重视,提交也可以碰到错误呀!
try{
Connection conn1 = getConnection1();
Connection conn2 = getConnection2();
// 基于conn1做插入操纵
// 基于conn2做更新操纵
try{
conn1.commit()
} catch(SQLExcetion ) {
conn1.rollback();
}
try {
conn2.commit();
} catch(SQLException ) {
conn2.rollbakc();
// 包管必然删除刚才插入的订单。
}
} catch(SQLException ex) {
// 若是插入失败,conn1.rollback
// 若是更新失败,conn1.rollback && conn2.rollback
} finally {
conn1.close();
conn2.close();
}
看看上述的代码就知道,其实操纵很是的错杂,甚至:包管必然删除刚才插入的订单底子无法包管。
在上述景象下的事务可以称之为分布式事务,经由过程上述的代码中事务同时提交处理惩罚的项目组我们可以得出,要想处理惩罚分布式事务,必须有自力于数据库的第三方的事务处理惩罚组件。
荣幸的是凡是景象下,JavaEE兼容的应用办事器,例如:Weblogic,Websphere,JBoss,Glassfish等都有这种分布式事务处理惩罚的组件。
如何应用应用办事器的分布式事务经管器处理惩罚分布式事务?
以galssfish为例
1 建树对应两个数据库的XA(javax.sql.XADataSource)类型的数据源。
2 应用UserTransaction来包管分布式事务。
try{
Connection conn1 = datasource1.getConnection();
Connection conn2 = datasource2.getConnection();
UserTransaction ut = getUserTransaction();
ut.begin();
// 插入订单
// 批改库存
ut.commit(); // 成功的景象下,提交更新。
} catch(SQLException ex) {
ut.rollback(); // 失败的景象下,回滚所有的操纵
} finally {
conn.close();
}
如何获取UserTransaction呢?可以应用如下办法
UserTransaction tx = (UserTransaction) ctx.lookup(jndi/UserTransaction);
J2EE开辟人员应用数据接见对象(DAO)设计模式把底层的数据接见逻辑和高层的商务逻辑分隔。实现DAO模式可以或许加倍专注于编写数据接见代码。这篇文章中,Java开辟人员Sean C. Sullivan从三个方面评论辩论DAO编程的布局特点:事务划分,异常处理惩罚,日记记录。
在比来的18个月,我和一个优良的软件开辟团队一路工作,开辟定制基于WEB的供给链经管应用法度.我们的应用法度接见广泛的持久层数据,包含出货状况,供给链轨制,库存,货色发运,项目经管数据,和用户属性等.我们应用JDBC API连接我们公司的各类数据库平台,并且在全部应用法度中应用了DAO设计模式.
经由过程在全部应用法度中应用数据接见对象(DAO)设计模式使我们可以或许把底层的数据接见逻辑和上层的商务逻辑分隔.我们为每个数据源创建了供给CRUD(创建,读取,更新,删除)操纵的DAO类.
在本文中,我将向你介绍DAO的实现策略以及创建更好的DAO类的技巧.我会明白的介绍日记记录,异常处理惩罚,和事务划分三项技巧.你将学在你的DAO类中如何把这三种技巧连络在一路.这篇文章假设你熟悉JDBC API,SQL和关系性数据库编程.
我们先往返顾一下DAO设计模式和数据接见对象.
DAO根蒂根基
DAO模式是标准的J2EE设计模式之一.开辟人员应用这个模式把底层的数据接见操纵和上层的商务逻辑分隔.一个典范的DAO实现有下列几个组件:
1. 一个DAO工厂类;
2. 一个DAO接口;
3. 一个实现DAO接口的具体类;
4. 数据传递对象(有些时辰叫做值对象).
具体的DAO类包含了从特定的数据源接见数据的逻辑。鄙人面的这段中你将学到设计和实现数据接见对象的技巧。
事务划分:
关于DAO要记住的一件首要工作是它们是事务性对象。每个被DAO履行的操纵(象创建,更新、或删除数据)都是和事务相接洽关系的。同样的,事务划分(transaction demarcation)的概念是希罕首要的。
事务划分是在事务界定定义中的体式格式。J2EE规范为事务划分描述了两种模式:编程务(programmatic)和声明务(declarative)。下表是对这两种模式的拆分:
声明务划分
编程务划分
法度员应用EJB的布署描述符声明事务属性
法度员担当编写事务逻辑代码的义务。
运行时景象(EJB容器)应用这些属性来主动的经管事务。
应用法度经由过程一个API接口来把握事务。
我将把重视力集中的编程务划分上。
象前面的介绍一样,DAOs是一些事务对象。一个典范的DAO要履行象创建、更新、和删除这的事务性操纵。在设计一个DAO时,起首要问本身如下题目:
1、 事务将如何开端?
2、 事务将如何停止?
3、 那个对象将承担起动一个事务的义务?
4、 那个对象将承担停止一个事务的义务?
5、 DAO应当承担起动和停止事务的义务?
6、 应用法度须要交叉接见多个DAO吗?
7、 一个事务包含一个DAO还是多个DAO?
8、 一个DAO包含其它的DAO中的办法吗?
答复这些题目将有助于你为DAO对象选择好的事务划分策略。对ADO中的事务划分有两个首要的策略。一种办法是应用DAO承担事务划分的义务;另一种是延期务,它把事务划分到调用DAO对象的办法中。若是你选择前者,你将要在DAO类中嵌入事务代码。若是你选择后者,事务代码将被写在DAO类的 外部。我们将应用简单的代码实例来更好的懂得这两种办法是如何工作的。
实例1显现了一个带有两种数据操纵的DAO:创建(create)和更新():
public void createWarehouseProfile(WHProfile profile); public void WarehouseStatus(WHIdentifier id, StatusInfo status);
实例2显现了一个简单的事务,事务划分代码是在DAO类的外部。重视:在这个例子中的调用者把多个DOA操纵组合到这个事务中。
tx.begin(); // start the transaction dao.createWarehouseProfile(profile); dao.WarehouseStatus(id1, status1); dao.WarehouseStatus(id2, status2); tx.commit(); // end the transaction
这种事务事务划分策略对在一个单一事务中接见多个DAO的应用法度来说尤为首要。
你即可应用JDBC API也可以应用Java 事务API(JTA)来实现事务的划分。JDBC事务划分比JTA事务划分简单,然则JTA供给了更好的灵活性。鄙人面的这段中,我们会进一步的看事务划分机制。
应用JDBC的事务划分
JDBC事务是应用Connection对象来把握的。JDBC的毗邻接口(java.sql.Connection)供给了两种事务模式:主动提交和手动提交。Java.sql.Connection为把握事务供给了下列办法:
.public void setAutoCommit(Boolean) .public Boolean getAutoCommit() .public void commit() .public void rollback()
实例3显现如何应用JDBC API来划分事务:
import java.sql.; import javax.sql.; // ... DataSource ds = obtainDataSource(); Connection conn = ds.getConnection(); conn.setAutoCommit(false); // ... pstmt = conn.prepareStatement(;UPDATE MOVIES ...;); pstmt.setString(1, ;The Great Escape;); pstmt.executeUpdate(); // ... conn.commit(); // ...
应用JDBC事务划分,你可以或许把多个SQL语句组合到一个单一事务中。JDBC事务的毛病之一就是事务局限被限制在一个单一的数据库连接中。一个 JDBC事务不克不及够跨越多个数据库。接下来,我们会看到如何应用JTA来干事务划分的。因为JTA不象JDBC那样被广泛的懂得,所以我起首概要的介绍一 下JTA。
JTA概要介绍
Java事务API(JTA;Java Transaction API)和它的同胞Java事务办事(JTS;Java Transaction Service),为J2EE平台供给了分布式事务办事。一个分布式事务(distributed transaction)包含一个事务经管器(transaction manager)和一个或多个资料经管器(resource manager)。一个资料经管器(resource manager)是随便率性类型的持久化数据存储。事务经管器(transaction manager)承担着所有事务参与单位者的彼此通信的义务。下车站显示了事务经管器和资料经管的间的关系。
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限制在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:
.JDBC连接
.JDO PersistenceManager 对象
.JMS 队列
.JMS 主题
.企业JavaBeans(EJB)
.一个用J2EE Connector Architecture 规范编译的资料分派器。
应用JTA的事务划分
要用JTA来划分一个事务,应用法度调用javax.transaction.UserTransaction接口中的办法。示例4显示了一个典范的JNDI搜刮的UseTransaction对象。
import javax.transaction.; import javax.naming.; // ... InitialContext ctx = new InitialContext(); Object txObj = ctx.lookup(;java:comp/UserTransaction;); UserTransaction utx = (UserTransaction) txObj;
应用法度有了UserTransaction对象的引用之后,就可以象示例5那样来起动事务。
utx.begin(); // ... DataSource ds = obtainXADataSource(); Connection conn = ds.getConnection(); pstmt = conn.prepareStatement(;UPDATE MOVIES ...;); pstmt.setString(1, ;Spinal Tap;); pstmt.executeUpdate(); // ... utx.commit(); // ...
当应用法度调用commit()时,事务经管器应用两段提交和谈来停止事务。JTA事务把握的办法:
.javax.transaction.UserTransaction接口供给了下列事务把握办法:
.public void begin() .public void commit() .public void rollback() .public void getStatus() .public void setRollbackOnly() .public void setTransactionTimeout(int)
应用法度调用begin()来起动事务,即可调用commit()也可以调用rollback()来停止事务。
应用JTA和JDBC
开辟人员经常应用JDBC来作为DAO类中的底层数据操纵。若是规划应用JTA来划分事务,你将须要一个实现了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驱动。实现了这些接口的驱动将有才能参与到JTA事务中。一个XADataSource对象是一个XAConnection对象的工厂。XAConnections是参与到JTA事务中的连接。
你须要应用应用法度办事器经管对象来建树XADataSource对象。对于特别的指令请参考应用法度办事器文档和JDBC驱动文档。
J2EE应用法度应用JNDI来查找数据源。一旦应用法度有了一个数据源对象的引用,这会调用javax.sql.DataSource.getConnection()来获得数据库的连接。
XA连接差别于非XA连接。要记住的是XA连接是一个JTA事务中的参与者。这就意味着XA连接不支撑JDBC的主动提交特点。也就是说应用法度不必 在XA连接上调用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,应 用法度应当应用UserTransaction.begin()、UserTransaction.commit()和 UserTransaction.rollback().
选择好的办法
我们已经评论辩论了JDBC和JTA是如何划分事务的。每一种办法都有它的长处,回此你须要决意为你的应用法度选择一个最适应的办法。在我们团队很多比来的对于事务划分的项目中应用JDBC API来创建DAO类。这DAO类总结如下:
.事务划分代码被嵌入到DAO类内部
.DAO类应用JDBC API来进行事务划分
.调用者没有划分事务的办法
.事务局限被限制在一个单一的JDBC连接
JDBC事务对错杂的企业应用法度不老是有效的。若是你的事务将跨越多个DAO对象或多个数据库,那么下面的实现策略可能会更恰当:
.用JTA对事务进行划分
.事务划分代码被DAO分隔
.调用者承担划分事务的义务
.DAO参与一个全局的事务中
JDBC办法因为它的简略单纯性而具有吸引力,JTA办法供给了更多灵活性。你选择什么样的实现将依附于你的应用法度的特定需求。
日记记录和DAO
一个好的DAO实现类将应用日记记录来捕获有关它在运行时的行动细节。你可以选择记录异常、设备信息、连接状况、JDBC驱动法度的元数据或查询参数。日记对开辟全部阶段都是有益的。我经常搜检应用法度在开辟时代、测试时代和产品中的日记记录。
在这段中,我们将显现一段如何把Jakarta Commaons Logging连络中一个DAO中的例子。在我们开端之前,让我们先回顾一些根蒂根基常识。
选择一个日记例库
很多开辟人员应用的基本日记情势是:System.out.println和System.err.println.Println语句。这种情势快捷便利,但它们不克不及供给一个完全的日记体系的的才能。下表列出了Java平台的日记类库:
日记类库
开源吗?
URL
Java.util.logging
否
http://java.sun.com/j2ee
Jakarta Log4j
是
http://hajarta.apache.org/log4j/
Jakarta Commons Logging
是
http:/Jakarta.apache.org/commons/logging.html
Java.util.logging是J2SE1.4平台上的标准的API。然则,大多半开辟人员都认为Jakarta Log4j供给了更大的功能性和灵活性。Log4j超出java.util.logging的长处之一就是它支撑J2SE1.3和J2SE1.4平台。
Jakarta Commons Logging可以或许被用于和java.util.loggin或Jakarta Log4j一路工作。Commons Logging是一个把你的应用法度自力于日记实现的提取层。应用Commons Logging你可以或许经由过程改变一个设备文件来与下面的日记实现来互换数据。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一个日记示例
示例7显示了在一个DOA类中如何应用Jakarta Commons Logging
import org.apache.commons.logging.; class DocumentDAOImpl implements DocumentDAO { static private final Log log = LogFactory.getLog(DocumentDAOImpl.class); public void Document(String id) { // ... log.debug(;deleting document: ; + id); // ... try { // ... data operations ... } catch (SomeException ex) { log.error(;Unable to document; ex); // ... handle the exception ... } } }
日记是评估应用法度的根蒂根基项目组。若是你在一个DAO中碰到了失败,日记经常会为懂得产生的什么错误供给好的信息。把日记连络到你的DAO中,确保获得调试和解决题目的有效手段。
DAO中的异常处理惩罚
我们已经看了事务划分和日记记录,并且如今对于它们是如何应用于数据接见对象的有一个深切的懂得。我们第三项目组也是最后要评论辩论的是异常处理惩罚。下面的一些简单的异常处理惩罚方针应用你的DAO更轻易应用,加倍结实和更具有可保护性。
在实现DAO模式的时辰,要考滤下面的题目:
.在DAO的public接口中的办法将抛出被搜检的异常吗?
.若是是,将抛出什么样的搜检性异常?
.在DAO实现类中怎能样处理惩罚异常。
在用DAO模式工作的过程中,我们的团队为异常处理惩罚开辟了一组方针。下面的这些方针会很大程度的改良你的DAO:
.DAO办法应当抛出有意义的异常。
.DAO办法不该该抛出java.lang.Exception异常。因为java.lang.Exception太一般化,它不克不及包含有关潜伏题目的所有信息。
.DAO办法不该该抛出java.sql.SQLException异常。SQLException是一个底层的JDBC异常,DAO应用尽力封装JDBC异常而不该该把JDBC异常留给应用法度的其它项目组。
.在DAO接口中的办法应当只抛出调用者期望处理惩罚的搜检性异常。若是调用者不克不及用恰当的办法来处理惩罚异常,考滤抛出不搜检性(运行时run-time)异常。
.若是你的数据接见代码捕获了一个异常,不成要忽视它。忽视捕获异常的DAO是很处理惩罚的。
.应用异常链把底层的异常传递给高层的某个处理惩罚器。
.考滤定义一个标准的DAO异常类。Spring框架供给了一个优良的预定义的DAO异常类的凑集。
看Resources,查看有异常和异常处理惩罚技巧的更具体信息。
实现示例:MovieDAO
MoveDAO是一个示范了在这篇文章中所评论辩论的所有技巧,包含事务划分、日记记录和异常处理惩罚。你会在Resources段找到MovieDAO的源代码。它被分下面的三个包:
.daoexamples.exception .daoexamples.move .daoexamples.moviedemo
这个DAO模式的实现由下面的类和接口构成:
.daoexamples.movie.MovieDAOFactory .daoexamples.movie.MovieDAO .daoexamples.movie.MovieDAOImpl .daoexamples.movie.MovieDAOImplJTA .daoexamples.movie.Movie .daoexamples.movie.MovieImple .daoexamples.movie.MovieNotFoundException .daoexamples.movie.MovieUtil
MovieDAO接口定义了DAO的数据操纵。这个接口有如下五个办法:
.public Movie findMovieById(String id) .public java.util.Collection findMoviesByYear(String year) .public void Movie(String id) .public Movie createMovie(String rating,String year,String title) .public void Movie(String id,String rating,String year,String title)
daoexamples.movie包包含了两个MovieDAO接口的实现。每个实现应用了一个同的事务划分办法,如下表所示:
MovieDAOImpl
MovieDAOImplJTA
实现了MovieDAO接口吗?
Yes
Yes
经由过程JNDI获得DataSource吗?
Yes
Yes
从一个DataSource获得java.sql.Connection对象吗?
Yes
Yes
DAO界定内部的事务吗?
Yes
No
应用JDBC事务吗?
Yes
No
应用一个XA DataSource吗?
No
Yes
分担JTA事务吗?
No
Yes
MovieDAO 示范应用法度
这个示范应用法度是一个叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet类,它应用Movie DAO来查询和更新一个表中的movie数据。
这个servlet示范了把JTA感知的MovieDAO和Java消息办事组合到一个单一的事务中,如示例8所示:
UserTransaction utx = MovieUtil.getUserTransaction(); utx.begin(); batman = dao.createMovie(;R; ;2008; ;Batman Reloaded;); publisher = new MessagePublisher(); publisher.publishTextMessage(;I’ll be back;); dao.Movie(topgun.getId(), ;PG-13; topgun.getReleaseYear(), topgun.getTitle()); dao.Movie(legallyblonde.getId()); utx.commit();
要运行这个典范应用法度,在你的应用法度办事器中设备一个XA 数据源和一个非XA数据源。然后布署daoexamples.ear文件。这个应用法度将运行在任何与J2EE兼容的应用法度办事器。
事务处理惩罚
信息是任何企事业单位的首要资产,任何企业项目组都包含着信息的流入、流出,任何企业项目组都把握着某些信息。同时,信息必须在恰当的机会传播给须要的 人。并且,信息还须要安然束缚,凡是按照信息的类型和内容实验接见把握。为了包管数据的安然有效和正确靠得住,数据库经管体系(DBMS)必须供给同一的数 据保护功能。
事务是现代数据库理论中的核心概念之一。若是一组处理惩罚步调或者全部产生或者一步也不履行,我们称该组处理惩罚步调为一个事务。当所有的步调像一个操纵一样被完全地履行,我们称该事务被提交。因为此中的一项目组或多步履行失败,导致没有步调被提交,则事务必须回滚(回到最初的体系状况)。事务必须功用 ISO/IEC所制订的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久 性(durability)的缩写。事务的原子性默示事务履行过程中的任何失败都将导致事务所做的任何批改失效。一致性默示当事务履行失败时,所有被该事务影响的数据都应当恢复到事务履行前的状况。隔离性默示在事务履行过程中对数据的批改,在事务提交之前对其他事务不成见。持久性默示已提交的数据在事务执 行失败时,数据的状况都应当正确。
鄙人面我们列举一个应用SQL Server数据库进行事务处理惩罚的例子。主表是一个规章轨制信息表(bylaw),首要字段有记录编号、题目、作者、书写日期等。两个子表分别是附件表 (bylaw_affix)和文本信息表(bylaw_content)。表布局见图1所示。bylaw表的记录编号与bylaw_affix表的记录编 号、bylaw_content表的记录编号是对应的,每次对规章轨制信息的操纵也就是对这三个表的结合操纵。例如要删除规章轨制中的一笔记录,若是不应用事务,就可能会呈现如许的景象:第一个表中成功删除后,数据库忽然呈现不测状况,而第二、三个表中的操纵没有完成,如许,删除操纵并没有完成,甚至已经 破损数据库中的数据。要避免这种景象,就应当应用事务,它的感化是:要么三个表都操纵成功,要么都失败。换句话说,就是对峙数据的一致性。所以,为了确保对数据操纵的完全和一致,在法度设计时要充沛推敲到事务处理惩罚方面的题目。
图1 示例表布局
Java中的事务处理惩罚
一般景象下,J2EE应用办事器支撑JDBC事务、JTA(Java Transaction API)事务、容器经管事务。一般景象下,好不要在法度中同时应用上述三种事务类型,比如在JTA事务中嵌套JDBC事务。第二方面,事务要在尽可能短 的时候内完成,不要在不合办法中实现事务的应用。下面我们列举两种事务处理惩罚体式格式。
1、JavaBean中应用JDBC体式格式进行事务处理惩罚 在JDBC中如何将多个SQL语句组合成一个事务呢?在JDBC中,打开一个连接对象Connection时,缺省是auto-commit模式,每个 SQL语句都被算作一个事务,即每次履行一个语句,都邑主动的获得事务确认。为了能将多个SQL语句组合成一个事务,要将auto-commit模式樊篱 掉。在auto-commit模式樊篱掉之后,若是不调用commit()办法,SQL语句不会获得事务确认。在比来一次commit()办法调用之后的 所有SQL会在办法commit()调用时获得确认。
public int (int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交体式格式 dbc.executeUpdate( bylaw where ID= + sID); dbc.executeUpdate( bylaw _content where ID= + sID); dbc.executeUpdate( bylaw _affix where bylawid= + sID); con.commit();//提交JDBC事务 con.setAutoCommit(true);// 恢复JDBC事务的默认提交体式格式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
2、SessionBean中的JTA事务 JTA 是事务办事的 J2EE 解决规划。本质上,它是描述事务接口(比如 UserTransaction 接口,开辟人员直接应用该接口或者经由过程 J2EE 容器应用该接口来确保营业逻辑可以或许靠得住地运行)的 J2EE 模型的一项目组。JTA 具有的三个首要的接口分别是 UserTransaction 接口、TransactionManager 接口和 Transaction 接口。这些接口共享公共的事务操纵,例如 commit() 和 rollback(), 然则也包含特别的事务操纵,例如 suspend(),resume() 和 enlist(),它们只呈如今特定的接口上,以便在实现中容许必然程度的接见把握。例如,UserTransaction 可以或许履行事务划分和根蒂根基的事务操纵,而 TransactionManager 可以或许履行高低文经管。 应用法度可以调用UserTransaction.begin()办法开端一个事务,该事务与应用法度正在此中运行的当火线程相接洽关系。底层的事务经管器实际处理惩罚线程与事务之间的接洽关系。UserTransaction.commit()办法终止与当火线程接洽关系的事务。 UserTransaction.rollback()办法将放弃与当火线程接洽关系的当前事务。
public int (int sID) { DataBaseConnection dbc = null; dbc = new DataBaseConnection(); dbc.getConnection(); UserTransaction transaction = sessionContext.getUserTransaction();//获得JTA事务 try { transaction.begin(); //开端JTA事务 dbc.executeUpdate( bylaw where ID= + sID); dbc.executeUpdate( bylaw _content where ID= + sID); dbc.executeUpdate( bylaw _affix where bylawid= + sID); transaction.commit(); //提交JTA事务 dbc.close(); return 1; } catch (Exception exc) { try { transaction.rollback();//JTA事务回滚 } catch (Exception ex) { //JTA事务回滚失足处理惩罚 ex.printStackTrace(); } exc.printStackTrace(); dbc.close(); return -1; } }
Cant start a cloned connection while in manual transaction mode错误2008-03-13 20:30呈现Cant start a cloned connection while in manual transaction mode错误,从网上找到原因及解决办法如下: 原因一般是当你在一个SQL SERVER的JDBC连接上履行多个STATEMENTS的操纵,或者是手动事务状况(AutoCommit=false) 并且应用默认的模式. direct (SelectMethod=direct) 模式.
解决办法 当你应用手动事务模式时,必须把SelectMethod 属性的值设置为 Cursor, 或者是确保在你的连接只有一个STATEMENT操纵。
批改url
参加SelectMethod=cursor即可
如:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=ys;SelectMethod=Cursor;User=ys;Password=ys); package _class; import java.sql.; import java.util.StringTokenizer; public class connDB{ String sDBDriver = com.microsoft.jdbc.sqlserver.SQLServerDriver; String sConnStr = jdbc:microsoft:sqlserver://127.0.0.1:1433;SelectMethod=cursor;DatabaseName=myDB;user=sa;password=river; Connection cn = null; Statement stmt; boolean autoCommit; private String DbType=MYSQL; //private String DbType=Oracle; private connDB(){ init(); } private void init(){ try{ Class.forName(sDBDriver).newInstance(); cn = DriverManager.getConnection(sConnStr); }catch(Exception e){ System.err.println(conndb():连接异常. + e.getMessage()); } } public static connDB getNewInstance(){ return new connDB(); } //数据绑定的材料如同很少,有空给大师一个例子。在这里只能返回PreparedStatement。 public PreparedStatement getPreparedStmt(String sql) throws SQLException{ PreparedStatement preStmt=null; try{ preStmt = cn.prepareStatement(sql); }catch(SQLException ex){ ex.printStackTrace(); throw ex; } return preStmt; } public void beginTrans() throws SQLException{ try{ autoCommit=cn.getAutoCommit(); cn.setAutoCommit(false); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(beginTrans Errors); throw ex; } } public void commit()throws SQLException{ try{ cn.commit(); cn.setAutoCommit(autoCommit); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(Commit Errors); throw ex; } } public void rollback(){ try{ cn.rollback(); cn.setAutoCommit(autoCommit); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(Rollback Errors); //throw ex; } } public boolean getAutoCommit() throws SQLException{ boolean result=false; try{ result=cn.getAutoCommit(); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(getAutoCommit fail+ex.getMessage()); throw ex; } return result; } //默认的景象下一次executeQuery(String sql)是一次事务。 //然则可以调用beginTrans(),然后多次executeQuery(String sql),最后commit()实现多sql的事务处理惩罚(重视在这种景象下若是产生违例,切切不要忘了在catch(){调用rollBack()})。 // public ResultSet executeQuery(String sql) throws SQLException{ ResultSet rs = null; try{ stmt=cn.createStatement(); rs = stmt.executeQuery(sql); } catch(SQLException ex) { ex.printStackTrace(); System.out.println(conndb.executeQuery:+ex.getMessage()); throw ex; } return rs; } public void executeUpdate(String sql) throws SQLException{ try{ stmt=cn.createStatement(); stmt.executeUpdate(sql); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(conndb.executeUpdate:+ex.getMessage()); throw ex; } } //Method doBatch 的参数sql,是由一些sql语句拼起来的,用;隔开。可以将很多的sql放在一个事务中,一次履行。 public int[] doBatch(String sql) throws SQLException{ int[] rowResult=null; String a; try{ //boolean autoCommit=cn.getAutoCommit(); //cn.setAutoCommit(false); stmt=cn.createStatement(); StringTokenizer st = new StringTokenizer(sql,;); while (st.hasMoreTokens()){ a=st.nextToken(); stmt.addBatch(a); } rowResult=stmt.executeBatch(); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(conndb.doBatch:+ex.getMessage()); throw ex; } return rowResult; } public String getDbType(){ return DbType; } public void close() throws SQLException{ try{ stmt.close(); stmt=null; cn.close(); cn=null; }catch(SQLException ex){ ex.printStackTrace(); System.out.println(Closeing connection fail+ex.getMessage()); throw ex;
} } public static void main(String[] args)throws Exception{ connDB con=connDB.getNewInstance(); System.out.println(con.getDbType()); String sql2= into test values(0510321315,李白,80);; String s1= test; con.beginTrans(); ResultSet rs=con.executeQuery(s1); con.executeUpdate(sql2);System.out.println(con.executeUpdate(sql2);); /try{ int up=s.executeUpdate(sql2); if(up!=0)System.out.println(语句:+sql2+插入成功!); else System.out.println(语句:+sql2+插入失败!); }catch(SQLException e){System.out.println(e);}/ //ResultSet rs=s.executeQuery( titles); con.executeUpdate( test where sno=0510321315);System.out.println(con.executeUpdate(\ test where sno=0510321315\);); con.commit(); while(rs.next()){ System.out.print(rs.getString(1)+\t); System.out.print(rs.getString(2)+\t); System.out.print(rs.getInt(3)+\t); System.out.println( ); }
con.close(); } }
注:在Java中应用事务处理惩罚,起重请求数据库支撑事务。如应用MySQL的事务功能,就请求MySQL的表类型为Innodb才支撑事务。不然,在Java法度中做了commit或rollback,但在数据库中底子不克不及生效。
JavaBean中应用JDBC体式格式进行事务处理惩罚
public int (int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交体式格式 dbc.executeUpdate( xiao where ID= + sID); dbc.executeUpdate( xiao_content where ID= + sID); dbc.executeUpdate( xiao_affix where bylawid= + sID); con.commit();//提交JDBC事务 con.setAutoCommit(true);// 恢复JDBC事务的默认提交体式格式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
在数据库操纵中,一项事务是指由一条或多条对数据库更新的sql语句所构成的一个不成分别的工作单位。只有当事务中的所有操纵都正常完成了,全部事务才干被提交到数据库,若是有一项操纵没有完成,就必须作废全部事务。
例如在银行的转帐事务中,假定张三从本身的帐号上把1000元转到李四的帐号上,相干的sql语句如下:
account set monery=monery-1000 where name=zhangsan
account set monery=monery+1000 where name=lisi
这个两条语句必须作为一个完成的事务来处理惩罚。只有当两条都成功履行了,才干提交这个事务。若是有一句失败,全部事务必须作废。
在connection类中供给了3个把握事务的办法:
(1) setAutoCommit(Boolean autoCommit):设置是否主动提交事务;
(2) commit();提交事务;
(3) rollback();作废事务;
在jdbc api中,默认的景象为主动提交事务,也就是说,每一条对数据库的更新的sql语句代表一项事务,操纵成功后,体系主动调用commit()来提交,不然将调用rollback()来作废事务。
在jdbc api中,可以经由过程调用setAutoCommit(false) 来禁止主动提交事务。然后就可以把多条更新数据库的sql语句做为一个事务,在所有操纵完成之后,调用commit()来进行整体提交。假如此中一项 sql操纵失败,就不会履行commit()办法,而是产生响应的sqlexception,此时就可以捕获异常代码块中调用rollback()办法作废事务。
事务处理惩罚是企业应用须要解决的最首要的题目之一。J2EE经由过程JTA供给了完全的事务经管才能,包含多个事务性资料的经管才能。然则大项目组应用都是运行在单一的事务性资料之上(一个数据库),他们并不须要全局性的事务办事。本地事务办事已然足够(比如JDBC事务经管)。 本文并不评论辩论应当采取何种事务处理惩罚体式格式,首要目标是评论辩论如何更为优雅地设计事务办事。仅以JDBC事务处理惩罚为例。涉及到的DAO,Factory,Proxy,Decorator等模式概念,请浏览相干材料。 也许你据说过,事务处理惩罚应当做在service层,也许你也正如许做,然则否知道为什么如许做?为什么不放在DAO层干事务处理惩罚。显而易见的原因是营业层 接口的每一个办法有时辰都是一个营业用例(User Case),它须要调用不合的DAO对象来完成一个营业办法。比如简单地以网上书店购书最后的断定定单为例,营业办法起首是调用BookDAO对象(一般 是经由过程DAO工厂产生),BookDAO断定是否还有库存余量,取得该书的价格信息等,然后调用 CustomerDAO从帐户扣除响应的费用以及记录信息,然后是其他办事(通知经管员等)。简化营业流程可能如此: 重视,我们的例子忽视了连接的处理惩罚,只要包管同一个线程内取的是雷同的连接即可(可用ThreadLocal实现):
起首是营业接口,针对接口,而不是针对类编程:
public interface BookStoreManager{ public boolean buyBook(String bookId,int quantity)throws SystemException; ....其他营业办法 }
接下来就是营业接口的实现类??营业对象:
public class BookStoreManagerImpl implements BookStoreManager{ public boolean buyBook(String bookId)throws SystemException{ Connection conn=ConnectionManager.getConnection();//获取数据库连接 boolean b=false; try{ conn.setAutoCommit(false); //作废主动提交 BookDAO bookDAO=DAOFactory.getBookDAO(); CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); //测验测验从库存中取书 if(BookDAO.reduceInventory(conn,bookId,quantity)){ BigDecimal price=BookDAO.getPrice(bookId); //取价格 //从客户帐户中扣除pricequantity的费用 b= CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); .... 其他营业办法,如通知经管员,生成定单等. ... conn.commit(); //提交事务 conn.setAutoCommit(true); } }catch(SQLException e){ conn.rollback(); //呈现异常,回滚事务 con.setAutoCommit(true); e.printStackTrace(); throws new SystemException(e); } return b; } }
然后是营业代表工厂:
public final class ManagerFactory { public static BookStoreManager getBookStoreManager() { return new BookStoreManagerImpl(); } }
如许的设计很是合适于DAO中的简单活动,我们项目中的一个小体系也是采取如许的设计规划,然则它不合适于更大范围的应用。起首,你有没有闻到代码反复的 bad smell?每次都要设置AutoCommit为false,然后提交,呈现异常回滚,包装异常抛到上层,写多了不烦才怪,那能不克不及打消呢?其次,营业代 表对象如今知道它内部事务经管的所有的细节,这与我们设计营业代表对象的初志不符。对于营业代表对象来说,懂得一个与事务有关的营业束缚是相当恰当的,但 是让它负责来实现它们就不太恰当了。再次,你是否想过嵌套营业对象的场景?营业代表对象之间的互相调用,层层嵌套,此时你又如何处理惩罚呢?你要知道按我们现 在的体式格式,每个营业办法都处于各自自力的事务高低文傍边(Transaction Context),互相调用形成了嵌套事务,此时你又该如何处理惩罚?也许办法就是从头写一遍,把不合的营业办法集中成一个巨无霸包装在一个事务高低文中。
我们有更为优雅的设计来解决这类题目,若是我们把Transaction Context的把握交给一个被营业代表对象、DAO和其他Component所共知的外部对象。当营业代表对象的某个办法须要事务经管时,它提示此外部 对象它开端一个事务,外部对象获取一个连接并且开端数据库事务。也就是将事务把握从service层抽离,当 web层调用service层的某个营业代表对象时,返回的是一个经过Transaction Context外部对象包装(或者说)的营业对象。此对象将恳求发送给原始营业代表对象,然则对此中的营业办法进行事务把握。那么,我们如何实现 此结果呢?答案是JDK1.3引进的技巧。技巧只能接口,这也是为什么我们须要营业接口BookStoreManager的原因。 起首,我们引入这个Transaction Context外部对象,它的代码其实很简单,若是不懂得技巧的请先浏览其他材料。 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
import java.sql.Connection;
import com.strutslet.demo.service.SystemException;
public final class TransactionWrapper {
/ 装潢原始的营业代表对象,返回一个与营业代表对象有雷同接口的对象 / public static Object decorate(Object delegate) { return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), new XAWrapperHandler( delegate)); } //技巧 static final class XAWrapperHandler implements InvocationHandler { private final Object delegate;
XAWrapperHandler(Object delegate) { this.delegate = delegate; } //简单起见,包装营业代表对象所有的营业办法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; Connection con = ConnectionManager.getConnection(); try { //开端一个事务 con.setAutoCommit(false); //调用原始营业对象的营业办法 result = method.invoke(delegate, args); con.commit(); //提交事务 con.setAutoCommit(true); } catch (Throwable t) { //回滚 con.rollback(); con.setAutoCommit(true); throw new SystemException(t); }
return result; } } }
正如我们所见,此对象只不过把营业对象须要事务把握的营业办法中的事务把握项目组抽取出来罢了。请重视,营业代表对象内部调用自身的办法将不会开端新的事务,因为这些调用不会传给对象。如此,我们去除了代表反复的味道。此时,我们的营业代表对象批改成:
public class BookStoreManagerImpl implements BookStoreManager { public boolean buyBook(String bookId)throws SystemException{ Connection conn=ConnectionManager.getConnection();// 获取数据库连接 boolean b=false; try{ BookDAO bookDAO=DAOFactory.getBookDAO(); CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); // 测验测验从库存中取书 if(BookDAO.reduceInventory(conn,bookId,quantity)){ BigDecimal price=BookDAO.getPrice(bookId); // 取价格 // 从客户帐户中扣除pricequantity的费用 b= CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); .... 其他营业办法,如通知经管员,生成定单等. ... } }catch(SQLException e){ throws new SystemException(e); } return b; } .... }
可以看到,此时的营业代表对象专注于实现营业逻辑,它不再关怀事务把握细节,把它们全部委托给了外部对象。营业代表工厂也批改一下,让它返回两种类型的营业代表对象:
public final class ManagerFactory { //返回一个被包装的对象,有事务把握才能 public static BookStoreManager getBookStoreManagerTrans() { return (BookStoreManager) TransactionWrapper .decorate(new BookStoreManagerImpl()); } //原始版本 public static BookStoreManager getBookStoreManager() { return new BookStoreManagerImpl(); } ...... }
我们在营业代表工厂上供给了两种不合的对象生成办法:一个用于创建被包装的对象,它会为每次办法调用创建一个新的事务;别的一个用于创建未被包装的版本,它用于参加到已有的事务(比如其他营业代表对象的营业办法),解决了嵌套营业代表对象的题目。 我们的设计还不敷优雅,比如我们默认所有的营业代表对象的办法调用都将被包装在一个Transaction Context。可事实是很多办法也许并不须要与数据库打交道,若是我们能设备哪些办法须要事务声明,哪些不须要事务经管就更完美了。解决办法也很简单, 一个XML设备文件来设备这些,调用时断定即可。说到这里,懂得spring的可能都邑意识到这不恰是声明式事务把握吗?恰是如此,事务把握就是AOP的 一种办事,spring的声明式事务经管是经由过程AOP实现的。AOP的实现体式格式包含:技巧,字节码生成技巧(如CGLIB库),java代码生成 (早期EJB采取),批改类装载器以及源代码级此外代码混淆织入(aspectj)等。我们这里就是哄骗了技巧,只能对接口;对类的 可以应用cglib库
简单事务的概念
我不想从道理上申明什么是事务,应为那太呆板了。我只想从一个简单的例子来申明什么是事务。
例如我们有一个订单库存经管体系,每一次生成订单的同时我们都要消减库存。凡是来说订单和库存在数据库里是分两张表来保存的:订单表,库存表。每一次我们追加一个订单实际上须要两步操纵:在订单表中插入一条数据,同时批改库存的数据。
如许题目来了,例如我们须要一个单位为10的订单,库存中有30件,幻想的操纵是我们在订单表中插入了一条单位为10的订单,之后将库存表中的数据修 改为20。然则有些时辰工作并不是老是遵守你的设法产生,例如:在你批改库存的时辰,数据库忽然因为莫名其妙的原因无法连接上了。也就是说库存更新失败了。然则订单已经产生了,那么怎么办呢?没办法,只有手动的批改。所以好的体式格式是将订单插入的操纵和库存批改的操纵绑定在一路,必须同时成功或者什么都 不做。这就是事务。
Java如何处理惩罚事务呢?
我们从java.sql.Connection说起,Connection默示了一个和数据库的链接,可以经由过程Connection来对数据库操纵。 在凡是景象是Connection的属性是主动提交的,也就是说每次的操纵真的更新了数据库,真的无法回退了。针对上述的例子,一旦库存更新失败了,订单无法回退,因为订单真的插入到了数据库中。这并不是我们的。
我们的是:看起来成功了,然则没有真的操纵数据库,知道我想让他真的产生。可以经由过程Connection的setAutoCommit (false)让Connection不主动提交你的数据,除非你真的想提交。那么如何让操纵真的产生呢?可以应用Connection的commit方 法。如何让操纵回退呢?应用rollback办法。
例如:
try{
Connection conn = getConnection(); // 不管如何我们获得了链接
conn.setAutoCommit(false);
// 插入订单
// 批改库存
conn.commit(); // 成功的景象下,提交更新。
} catch(SQLException ex) {
conn.rollback(); // 失败的景象下,回滚所有的操纵
} finally {
conn.close();
}
这里有一点很是首要,事务是基于数据库链接的。所以在但数据库的景象下,事务操纵很简单。
那么若是表分布在两个不合的数据库中呢?
例如订单表在订单库中,库存表在库存库中,那么我们如何处理惩罚如许的事务呢?
须要重视,提交也可以碰到错误呀!
try{
Connection conn1 = getConnection1();
Connection conn2 = getConnection2();
// 基于conn1做插入操纵
// 基于conn2做更新操纵
try{
conn1.commit()
} catch(SQLExcetion ) {
conn1.rollback();
}
try {
conn2.commit();
} catch(SQLException ) {
conn2.rollbakc();
// 包管必然删除刚才插入的订单。
}
} catch(SQLException ex) {
// 若是插入失败,conn1.rollback
// 若是更新失败,conn1.rollback && conn2.rollback
} finally {
conn1.close();
conn2.close();
}
看看上述的代码就知道,其实操纵很是的错杂,甚至:包管必然删除刚才插入的订单底子无法包管。
在上述景象下的事务可以称之为分布式事务,经由过程上述的代码中事务同时提交处理惩罚的项目组我们可以得出,要想处理惩罚分布式事务,必须有自力于数据库的第三方的事务处理惩罚组件。
荣幸的是凡是景象下,JavaEE兼容的应用办事器,例如:Weblogic,Websphere,JBoss,Glassfish等都有这种分布式事务处理惩罚的组件。
如何应用应用办事器的分布式事务经管器处理惩罚分布式事务?
以galssfish为例
1 建树对应两个数据库的XA(javax.sql.XADataSource)类型的数据源。
2 应用UserTransaction来包管分布式事务。
try{
Connection conn1 = datasource1.getConnection();
Connection conn2 = datasource2.getConnection();
UserTransaction ut = getUserTransaction();
ut.begin();
// 插入订单
// 批改库存
ut.commit(); // 成功的景象下,提交更新。
} catch(SQLException ex) {
ut.rollback(); // 失败的景象下,回滚所有的操纵
} finally {
conn.close();
}
如何获取UserTransaction呢?可以应用如下办法
UserTransaction tx = (UserTransaction) ctx.lookup(jndi/UserTransaction);
J2EE开辟人员应用数据接见对象(DAO)设计模式把底层的数据接见逻辑和高层的商务逻辑分隔。实现DAO模式可以或许加倍专注于编写数据接见代码。这篇文章中,Java开辟人员Sean C. Sullivan从三个方面评论辩论DAO编程的布局特点:事务划分,异常处理惩罚,日记记录。
在比来的18个月,我和一个优良的软件开辟团队一路工作,开辟定制基于WEB的供给链经管应用法度.我们的应用法度接见广泛的持久层数据,包含出货状况,供给链轨制,库存,货色发运,项目经管数据,和用户属性等.我们应用JDBC API连接我们公司的各类数据库平台,并且在全部应用法度中应用了DAO设计模式.
经由过程在全部应用法度中应用数据接见对象(DAO)设计模式使我们可以或许把底层的数据接见逻辑和上层的商务逻辑分隔.我们为每个数据源创建了供给CRUD(创建,读取,更新,删除)操纵的DAO类.
在本文中,我将向你介绍DAO的实现策略以及创建更好的DAO类的技巧.我会明白的介绍日记记录,异常处理惩罚,和事务划分三项技巧.你将学在你的DAO类中如何把这三种技巧连络在一路.这篇文章假设你熟悉JDBC API,SQL和关系性数据库编程.
我们先往返顾一下DAO设计模式和数据接见对象.
DAO根蒂根基
DAO模式是标准的J2EE设计模式之一.开辟人员应用这个模式把底层的数据接见操纵和上层的商务逻辑分隔.一个典范的DAO实现有下列几个组件:
1. 一个DAO工厂类;
2. 一个DAO接口;
3. 一个实现DAO接口的具体类;
4. 数据传递对象(有些时辰叫做值对象).
具体的DAO类包含了从特定的数据源接见数据的逻辑。鄙人面的这段中你将学到设计和实现数据接见对象的技巧。
事务划分:
关于DAO要记住的一件首要工作是它们是事务性对象。每个被DAO履行的操纵(象创建,更新、或删除数据)都是和事务相接洽关系的。同样的,事务划分(transaction demarcation)的概念是希罕首要的。
事务划分是在事务界定定义中的体式格式。J2EE规范为事务划分描述了两种模式:编程务(programmatic)和声明务(declarative)。下表是对这两种模式的拆分:
声明务划分
编程务划分
法度员应用EJB的布署描述符声明事务属性
法度员担当编写事务逻辑代码的义务。
运行时景象(EJB容器)应用这些属性来主动的经管事务。
应用法度经由过程一个API接口来把握事务。
我将把重视力集中的编程务划分上。
象前面的介绍一样,DAOs是一些事务对象。一个典范的DAO要履行象创建、更新、和删除这的事务性操纵。在设计一个DAO时,起首要问本身如下题目:
1、 事务将如何开端?
2、 事务将如何停止?
3、 那个对象将承担起动一个事务的义务?
4、 那个对象将承担停止一个事务的义务?
5、 DAO应当承担起动和停止事务的义务?
6、 应用法度须要交叉接见多个DAO吗?
7、 一个事务包含一个DAO还是多个DAO?
8、 一个DAO包含其它的DAO中的办法吗?
答复这些题目将有助于你为DAO对象选择好的事务划分策略。对ADO中的事务划分有两个首要的策略。一种办法是应用DAO承担事务划分的义务;另一种是延期务,它把事务划分到调用DAO对象的办法中。若是你选择前者,你将要在DAO类中嵌入事务代码。若是你选择后者,事务代码将被写在DAO类的 外部。我们将应用简单的代码实例来更好的懂得这两种办法是如何工作的。
实例1显现了一个带有两种数据操纵的DAO:创建(create)和更新():
public void createWarehouseProfile(WHProfile profile); public void WarehouseStatus(WHIdentifier id, StatusInfo status);
实例2显现了一个简单的事务,事务划分代码是在DAO类的外部。重视:在这个例子中的调用者把多个DOA操纵组合到这个事务中。
tx.begin(); // start the transaction dao.createWarehouseProfile(profile); dao.WarehouseStatus(id1, status1); dao.WarehouseStatus(id2, status2); tx.commit(); // end the transaction
这种事务事务划分策略对在一个单一事务中接见多个DAO的应用法度来说尤为首要。
你即可应用JDBC API也可以应用Java 事务API(JTA)来实现事务的划分。JDBC事务划分比JTA事务划分简单,然则JTA供给了更好的灵活性。鄙人面的这段中,我们会进一步的看事务划分机制。
应用JDBC的事务划分
JDBC事务是应用Connection对象来把握的。JDBC的毗邻接口(java.sql.Connection)供给了两种事务模式:主动提交和手动提交。Java.sql.Connection为把握事务供给了下列办法:
.public void setAutoCommit(Boolean) .public Boolean getAutoCommit() .public void commit() .public void rollback()
实例3显现如何应用JDBC API来划分事务:
import java.sql.; import javax.sql.; // ... DataSource ds = obtainDataSource(); Connection conn = ds.getConnection(); conn.setAutoCommit(false); // ... pstmt = conn.prepareStatement(;UPDATE MOVIES ...;); pstmt.setString(1, ;The Great Escape;); pstmt.executeUpdate(); // ... conn.commit(); // ...
应用JDBC事务划分,你可以或许把多个SQL语句组合到一个单一事务中。JDBC事务的毛病之一就是事务局限被限制在一个单一的数据库连接中。一个 JDBC事务不克不及够跨越多个数据库。接下来,我们会看到如何应用JTA来干事务划分的。因为JTA不象JDBC那样被广泛的懂得,所以我起首概要的介绍一 下JTA。
JTA概要介绍
Java事务API(JTA;Java Transaction API)和它的同胞Java事务办事(JTS;Java Transaction Service),为J2EE平台供给了分布式事务办事。一个分布式事务(distributed transaction)包含一个事务经管器(transaction manager)和一个或多个资料经管器(resource manager)。一个资料经管器(resource manager)是随便率性类型的持久化数据存储。事务经管器(transaction manager)承担着所有事务参与单位者的彼此通信的义务。下车站显示了事务经管器和资料经管的间的关系。
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限制在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:
.JDBC连接
.JDO PersistenceManager 对象
.JMS 队列
.JMS 主题
.企业JavaBeans(EJB)
.一个用J2EE Connector Architecture 规范编译的资料分派器。
应用JTA的事务划分
要用JTA来划分一个事务,应用法度调用javax.transaction.UserTransaction接口中的办法。示例4显示了一个典范的JNDI搜刮的UseTransaction对象。
import javax.transaction.; import javax.naming.; // ... InitialContext ctx = new InitialContext(); Object txObj = ctx.lookup(;java:comp/UserTransaction;); UserTransaction utx = (UserTransaction) txObj;
应用法度有了UserTransaction对象的引用之后,就可以象示例5那样来起动事务。
utx.begin(); // ... DataSource ds = obtainXADataSource(); Connection conn = ds.getConnection(); pstmt = conn.prepareStatement(;UPDATE MOVIES ...;); pstmt.setString(1, ;Spinal Tap;); pstmt.executeUpdate(); // ... utx.commit(); // ...
当应用法度调用commit()时,事务经管器应用两段提交和谈来停止事务。JTA事务把握的办法:
.javax.transaction.UserTransaction接口供给了下列事务把握办法:
.public void begin() .public void commit() .public void rollback() .public void getStatus() .public void setRollbackOnly() .public void setTransactionTimeout(int)
应用法度调用begin()来起动事务,即可调用commit()也可以调用rollback()来停止事务。
应用JTA和JDBC
开辟人员经常应用JDBC来作为DAO类中的底层数据操纵。若是规划应用JTA来划分事务,你将须要一个实现了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驱动。实现了这些接口的驱动将有才能参与到JTA事务中。一个XADataSource对象是一个XAConnection对象的工厂。XAConnections是参与到JTA事务中的连接。
你须要应用应用法度办事器经管对象来建树XADataSource对象。对于特别的指令请参考应用法度办事器文档和JDBC驱动文档。
J2EE应用法度应用JNDI来查找数据源。一旦应用法度有了一个数据源对象的引用,这会调用javax.sql.DataSource.getConnection()来获得数据库的连接。
XA连接差别于非XA连接。要记住的是XA连接是一个JTA事务中的参与者。这就意味着XA连接不支撑JDBC的主动提交特点。也就是说应用法度不必 在XA连接上调用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,应 用法度应当应用UserTransaction.begin()、UserTransaction.commit()和 UserTransaction.rollback().
选择好的办法
我们已经评论辩论了JDBC和JTA是如何划分事务的。每一种办法都有它的长处,回此你须要决意为你的应用法度选择一个最适应的办法。在我们团队很多比来的对于事务划分的项目中应用JDBC API来创建DAO类。这DAO类总结如下:
.事务划分代码被嵌入到DAO类内部
.DAO类应用JDBC API来进行事务划分
.调用者没有划分事务的办法
.事务局限被限制在一个单一的JDBC连接
JDBC事务对错杂的企业应用法度不老是有效的。若是你的事务将跨越多个DAO对象或多个数据库,那么下面的实现策略可能会更恰当:
.用JTA对事务进行划分
.事务划分代码被DAO分隔
.调用者承担划分事务的义务
.DAO参与一个全局的事务中
JDBC办法因为它的简略单纯性而具有吸引力,JTA办法供给了更多灵活性。你选择什么样的实现将依附于你的应用法度的特定需求。
日记记录和DAO
一个好的DAO实现类将应用日记记录来捕获有关它在运行时的行动细节。你可以选择记录异常、设备信息、连接状况、JDBC驱动法度的元数据或查询参数。日记对开辟全部阶段都是有益的。我经常搜检应用法度在开辟时代、测试时代和产品中的日记记录。
在这段中,我们将显现一段如何把Jakarta Commaons Logging连络中一个DAO中的例子。在我们开端之前,让我们先回顾一些根蒂根基常识。
选择一个日记例库
很多开辟人员应用的基本日记情势是:System.out.println和System.err.println.Println语句。这种情势快捷便利,但它们不克不及供给一个完全的日记体系的的才能。下表列出了Java平台的日记类库:
日记类库
开源吗?
URL
Java.util.logging
否
http://java.sun.com/j2ee
Jakarta Log4j
是
http://hajarta.apache.org/log4j/
Jakarta Commons Logging
是
http:/Jakarta.apache.org/commons/logging.html
Java.util.logging是J2SE1.4平台上的标准的API。然则,大多半开辟人员都认为Jakarta Log4j供给了更大的功能性和灵活性。Log4j超出java.util.logging的长处之一就是它支撑J2SE1.3和J2SE1.4平台。
Jakarta Commons Logging可以或许被用于和java.util.loggin或Jakarta Log4j一路工作。Commons Logging是一个把你的应用法度自力于日记实现的提取层。应用Commons Logging你可以或许经由过程改变一个设备文件来与下面的日记实现来互换数据。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一个日记示例
示例7显示了在一个DOA类中如何应用Jakarta Commons Logging
import org.apache.commons.logging.; class DocumentDAOImpl implements DocumentDAO { static private final Log log = LogFactory.getLog(DocumentDAOImpl.class); public void Document(String id) { // ... log.debug(;deleting document: ; + id); // ... try { // ... data operations ... } catch (SomeException ex) { log.error(;Unable to document; ex); // ... handle the exception ... } } }
日记是评估应用法度的根蒂根基项目组。若是你在一个DAO中碰到了失败,日记经常会为懂得产生的什么错误供给好的信息。把日记连络到你的DAO中,确保获得调试和解决题目的有效手段。
DAO中的异常处理惩罚
我们已经看了事务划分和日记记录,并且如今对于它们是如何应用于数据接见对象的有一个深切的懂得。我们第三项目组也是最后要评论辩论的是异常处理惩罚。下面的一些简单的异常处理惩罚方针应用你的DAO更轻易应用,加倍结实和更具有可保护性。
在实现DAO模式的时辰,要考滤下面的题目:
.在DAO的public接口中的办法将抛出被搜检的异常吗?
.若是是,将抛出什么样的搜检性异常?
.在DAO实现类中怎能样处理惩罚异常。
在用DAO模式工作的过程中,我们的团队为异常处理惩罚开辟了一组方针。下面的这些方针会很大程度的改良你的DAO:
.DAO办法应当抛出有意义的异常。
.DAO办法不该该抛出java.lang.Exception异常。因为java.lang.Exception太一般化,它不克不及包含有关潜伏题目的所有信息。
.DAO办法不该该抛出java.sql.SQLException异常。SQLException是一个底层的JDBC异常,DAO应用尽力封装JDBC异常而不该该把JDBC异常留给应用法度的其它项目组。
.在DAO接口中的办法应当只抛出调用者期望处理惩罚的搜检性异常。若是调用者不克不及用恰当的办法来处理惩罚异常,考滤抛出不搜检性(运行时run-time)异常。
.若是你的数据接见代码捕获了一个异常,不成要忽视它。忽视捕获异常的DAO是很处理惩罚的。
.应用异常链把底层的异常传递给高层的某个处理惩罚器。
.考滤定义一个标准的DAO异常类。Spring框架供给了一个优良的预定义的DAO异常类的凑集。
看Resources,查看有异常和异常处理惩罚技巧的更具体信息。
实现示例:MovieDAO
MoveDAO是一个示范了在这篇文章中所评论辩论的所有技巧,包含事务划分、日记记录和异常处理惩罚。你会在Resources段找到MovieDAO的源代码。它被分下面的三个包:
.daoexamples.exception .daoexamples.move .daoexamples.moviedemo
这个DAO模式的实现由下面的类和接口构成:
.daoexamples.movie.MovieDAOFactory .daoexamples.movie.MovieDAO .daoexamples.movie.MovieDAOImpl .daoexamples.movie.MovieDAOImplJTA .daoexamples.movie.Movie .daoexamples.movie.MovieImple .daoexamples.movie.MovieNotFoundException .daoexamples.movie.MovieUtil
MovieDAO接口定义了DAO的数据操纵。这个接口有如下五个办法:
.public Movie findMovieById(String id) .public java.util.Collection findMoviesByYear(String year) .public void Movie(String id) .public Movie createMovie(String rating,String year,String title) .public void Movie(String id,String rating,String year,String title)
daoexamples.movie包包含了两个MovieDAO接口的实现。每个实现应用了一个同的事务划分办法,如下表所示:
MovieDAOImpl
MovieDAOImplJTA
实现了MovieDAO接口吗?
Yes
Yes
经由过程JNDI获得DataSource吗?
Yes
Yes
从一个DataSource获得java.sql.Connection对象吗?
Yes
Yes
DAO界定内部的事务吗?
Yes
No
应用JDBC事务吗?
Yes
No
应用一个XA DataSource吗?
No
Yes
分担JTA事务吗?
No
Yes
MovieDAO 示范应用法度
这个示范应用法度是一个叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet类,它应用Movie DAO来查询和更新一个表中的movie数据。
这个servlet示范了把JTA感知的MovieDAO和Java消息办事组合到一个单一的事务中,如示例8所示:
UserTransaction utx = MovieUtil.getUserTransaction(); utx.begin(); batman = dao.createMovie(;R; ;2008; ;Batman Reloaded;); publisher = new MessagePublisher(); publisher.publishTextMessage(;I’ll be back;); dao.Movie(topgun.getId(), ;PG-13; topgun.getReleaseYear(), topgun.getTitle()); dao.Movie(legallyblonde.getId()); utx.commit();
要运行这个典范应用法度,在你的应用法度办事器中设备一个XA 数据源和一个非XA数据源。然后布署daoexamples.ear文件。这个应用法度将运行在任何与J2EE兼容的应用法度办事器。
事务处理惩罚
信息是任何企事业单位的首要资产,任何企业项目组都包含着信息的流入、流出,任何企业项目组都把握着某些信息。同时,信息必须在恰当的机会传播给须要的 人。并且,信息还须要安然束缚,凡是按照信息的类型和内容实验接见把握。为了包管数据的安然有效和正确靠得住,数据库经管体系(DBMS)必须供给同一的数 据保护功能。
事务是现代数据库理论中的核心概念之一。若是一组处理惩罚步调或者全部产生或者一步也不履行,我们称该组处理惩罚步调为一个事务。当所有的步调像一个操纵一样被完全地履行,我们称该事务被提交。因为此中的一项目组或多步履行失败,导致没有步调被提交,则事务必须回滚(回到最初的体系状况)。事务必须功用 ISO/IEC所制订的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久 性(durability)的缩写。事务的原子性默示事务履行过程中的任何失败都将导致事务所做的任何批改失效。一致性默示当事务履行失败时,所有被该事务影响的数据都应当恢复到事务履行前的状况。隔离性默示在事务履行过程中对数据的批改,在事务提交之前对其他事务不成见。持久性默示已提交的数据在事务执 行失败时,数据的状况都应当正确。
鄙人面我们列举一个应用SQL Server数据库进行事务处理惩罚的例子。主表是一个规章轨制信息表(bylaw),首要字段有记录编号、题目、作者、书写日期等。两个子表分别是附件表 (bylaw_affix)和文本信息表(bylaw_content)。表布局见图1所示。bylaw表的记录编号与bylaw_affix表的记录编 号、bylaw_content表的记录编号是对应的,每次对规章轨制信息的操纵也就是对这三个表的结合操纵。例如要删除规章轨制中的一笔记录,若是不应用事务,就可能会呈现如许的景象:第一个表中成功删除后,数据库忽然呈现不测状况,而第二、三个表中的操纵没有完成,如许,删除操纵并没有完成,甚至已经 破损数据库中的数据。要避免这种景象,就应当应用事务,它的感化是:要么三个表都操纵成功,要么都失败。换句话说,就是对峙数据的一致性。所以,为了确保对数据操纵的完全和一致,在法度设计时要充沛推敲到事务处理惩罚方面的题目。
图1 示例表布局
Java中的事务处理惩罚
一般景象下,J2EE应用办事器支撑JDBC事务、JTA(Java Transaction API)事务、容器经管事务。一般景象下,好不要在法度中同时应用上述三种事务类型,比如在JTA事务中嵌套JDBC事务。第二方面,事务要在尽可能短 的时候内完成,不要在不合办法中实现事务的应用。下面我们列举两种事务处理惩罚体式格式。
1、JavaBean中应用JDBC体式格式进行事务处理惩罚 在JDBC中如何将多个SQL语句组合成一个事务呢?在JDBC中,打开一个连接对象Connection时,缺省是auto-commit模式,每个 SQL语句都被算作一个事务,即每次履行一个语句,都邑主动的获得事务确认。为了能将多个SQL语句组合成一个事务,要将auto-commit模式樊篱 掉。在auto-commit模式樊篱掉之后,若是不调用commit()办法,SQL语句不会获得事务确认。在比来一次commit()办法调用之后的 所有SQL会在办法commit()调用时获得确认。
public int (int sID) { dbc = new DataBaseConnection(); Connection con = dbc.getConnection(); try { con.setAutoCommit(false);// 更改JDBC事务的默认提交体式格式 dbc.executeUpdate( bylaw where ID= + sID); dbc.executeUpdate( bylaw _content where ID= + sID); dbc.executeUpdate( bylaw _affix where bylawid= + sID); con.commit();//提交JDBC事务 con.setAutoCommit(true);// 恢复JDBC事务的默认提交体式格式 dbc.close(); return 1; } catch (Exception exc) { con.rollBack();//回滚JDBC事务 exc.printStackTrace(); dbc.close(); return -1; } }
2、SessionBean中的JTA事务 JTA 是事务办事的 J2EE 解决规划。本质上,它是描述事务接口(比如 UserTransaction 接口,开辟人员直接应用该接口或者经由过程 J2EE 容器应用该接口来确保营业逻辑可以或许靠得住地运行)的 J2EE 模型的一项目组。JTA 具有的三个首要的接口分别是 UserTransaction 接口、TransactionManager 接口和 Transaction 接口。这些接口共享公共的事务操纵,例如 commit() 和 rollback(), 然则也包含特别的事务操纵,例如 suspend(),resume() 和 enlist(),它们只呈如今特定的接口上,以便在实现中容许必然程度的接见把握。例如,UserTransaction 可以或许履行事务划分和根蒂根基的事务操纵,而 TransactionManager 可以或许履行高低文经管。 应用法度可以调用UserTransaction.begin()办法开端一个事务,该事务与应用法度正在此中运行的当火线程相接洽关系。底层的事务经管器实际处理惩罚线程与事务之间的接洽关系。UserTransaction.commit()办法终止与当火线程接洽关系的事务。 UserTransaction.rollback()办法将放弃与当火线程接洽关系的当前事务。
public int (int sID) { DataBaseConnection dbc = null; dbc = new DataBaseConnection(); dbc.getConnection(); UserTransaction transaction = sessionContext.getUserTransaction();//获得JTA事务 try { transaction.begin(); //开端JTA事务 dbc.executeUpdate( bylaw where ID= + sID); dbc.executeUpdate( bylaw _content where ID= + sID); dbc.executeUpdate( bylaw _affix where bylawid= + sID); transaction.commit(); //提交JTA事务 dbc.close(); return 1; } catch (Exception exc) { try { transaction.rollback();//JTA事务回滚 } catch (Exception ex) { //JTA事务回滚失足处理惩罚 ex.printStackTrace(); } exc.printStackTrace(); dbc.close(); return -1; } }
Cant start a cloned connection while in manual transaction mode错误2008-03-13 20:30呈现Cant start a cloned connection while in manual transaction mode错误,从网上找到原因及解决办法如下: 原因一般是当你在一个SQL SERVER的JDBC连接上履行多个STATEMENTS的操纵,或者是手动事务状况(AutoCommit=false) 并且应用默认的模式. direct (SelectMethod=direct) 模式.
解决办法 当你应用手动事务模式时,必须把SelectMethod 属性的值设置为 Cursor, 或者是确保在你的连接只有一个STATEMENT操纵。
批改url
参加SelectMethod=cursor即可
如:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=ys;SelectMethod=Cursor;User=ys;Password=ys); package _class; import java.sql.; import java.util.StringTokenizer; public class connDB{ String sDBDriver = com.microsoft.jdbc.sqlserver.SQLServerDriver; String sConnStr = jdbc:microsoft:sqlserver://127.0.0.1:1433;SelectMethod=cursor;DatabaseName=myDB;user=sa;password=river; Connection cn = null; Statement stmt; boolean autoCommit; private String DbType=MYSQL; //private String DbType=Oracle; private connDB(){ init(); } private void init(){ try{ Class.forName(sDBDriver).newInstance(); cn = DriverManager.getConnection(sConnStr); }catch(Exception e){ System.err.println(conndb():连接异常. + e.getMessage()); } } public static connDB getNewInstance(){ return new connDB(); } //数据绑定的材料如同很少,有空给大师一个例子。在这里只能返回PreparedStatement。 public PreparedStatement getPreparedStmt(String sql) throws SQLException{ PreparedStatement preStmt=null; try{ preStmt = cn.prepareStatement(sql); }catch(SQLException ex){ ex.printStackTrace(); throw ex; } return preStmt; } public void beginTrans() throws SQLException{ try{ autoCommit=cn.getAutoCommit(); cn.setAutoCommit(false); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(beginTrans Errors); throw ex; } } public void commit()throws SQLException{ try{ cn.commit(); cn.setAutoCommit(autoCommit); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(Commit Errors); throw ex; } } public void rollback(){ try{ cn.rollback(); cn.setAutoCommit(autoCommit); }catch(SQLException ex){ ex.printStackTrace(); System.out.print(Rollback Errors); //throw ex; } } public boolean getAutoCommit() throws SQLException{ boolean result=false; try{ result=cn.getAutoCommit(); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(getAutoCommit fail+ex.getMessage()); throw ex; } return result; } //默认的景象下一次executeQuery(String sql)是一次事务。 //然则可以调用beginTrans(),然后多次executeQuery(String sql),最后commit()实现多sql的事务处理惩罚(重视在这种景象下若是产生违例,切切不要忘了在catch(){调用rollBack()})。 // public ResultSet executeQuery(String sql) throws SQLException{ ResultSet rs = null; try{ stmt=cn.createStatement(); rs = stmt.executeQuery(sql); } catch(SQLException ex) { ex.printStackTrace(); System.out.println(conndb.executeQuery:+ex.getMessage()); throw ex; } return rs; } public void executeUpdate(String sql) throws SQLException{ try{ stmt=cn.createStatement(); stmt.executeUpdate(sql); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(conndb.executeUpdate:+ex.getMessage()); throw ex; } } //Method doBatch 的参数sql,是由一些sql语句拼起来的,用;隔开。可以将很多的sql放在一个事务中,一次履行。 public int[] doBatch(String sql) throws SQLException{ int[] rowResult=null; String a; try{ //boolean autoCommit=cn.getAutoCommit(); //cn.setAutoCommit(false); stmt=cn.createStatement(); StringTokenizer st = new StringTokenizer(sql,;); while (st.hasMoreTokens()){ a=st.nextToken(); stmt.addBatch(a); } rowResult=stmt.executeBatch(); }catch(SQLException ex){ ex.printStackTrace(); System.out.println(conndb.doBatch:+ex.getMessage()); throw ex; } return rowResult; } public String getDbType(){ return DbType; } public void close() throws SQLException{ try{ stmt.close(); stmt=null; cn.close(); cn=null; }catch(SQLException ex){ ex.printStackTrace(); System.out.println(Closeing connection fail+ex.getMessage()); throw ex;
} } public static void main(String[] args)throws Exception{ connDB con=connDB.getNewInstance(); System.out.println(con.getDbType()); String sql2= into test values(0510321315,李白,80);; String s1= test; con.beginTrans(); ResultSet rs=con.executeQuery(s1); con.executeUpdate(sql2);System.out.println(con.executeUpdate(sql2);); /try{ int up=s.executeUpdate(sql2); if(up!=0)System.out.println(语句:+sql2+插入成功!); else System.out.println(语句:+sql2+插入失败!); }catch(SQLException e){System.out.println(e);}/ //ResultSet rs=s.executeQuery( titles); con.executeUpdate( test where sno=0510321315);System.out.println(con.executeUpdate(\ test where sno=0510321315\);); con.commit(); while(rs.next()){ System.out.print(rs.getString(1)+\t); System.out.print(rs.getString(2)+\t); System.out.print(rs.getInt(3)+\t); System.out.println( ); }
con.close(); } }