![JAVA设计模式之事务处理[2],第1张 JAVA设计模式之事务处理[2],第1张](/aiimages/JAVA%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E4%BA%8B%E5%8A%A1%E5%A4%84%E7%90%86%5B2%5D.png)
import java lang reflect InvocationHandler; import java lang reflect Method; import java lang reflect Proxy;
import java sql Connection;
import 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 mit(); //提交事务 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库 这篇短文只是介绍下我对事务上下文模式以及声明式事务管理实现基本原理的理解 如有错误 请不吝赐教 谢谢 我的email:killme @gmail
lishixinzhi/Article/program/Java/gj/201311/27764
如果是JDBC其实很简单,就是先setAutoCommit(false);然后把你的数据库 *** 作语句都执行完了,再手动调用commit方法就行了,所谓的事务其实原则上很简单,尤其是关系型数据库,说白了就是所有的SQL成功了才提交,不成功就rollback仅此而已
我测试了用jdbc获得多个connection连接,可以实现,其实和连接一个数据库是一样的。
1、注册数据库驱动
2、用driverMagager获得数据库连接
3、获得connection之后就可以对数据库进行 *** 作了
对于需求,其实也是比较容易实现的。只要对于多个要commit的地方,加到同一个try块中,就可以了。
只要一个数据库存储出错,就会跳出该try,之后再 catch中执行对所以数据库的rollback。
类似:
try{
以上对于数据库的 *** 作代码略
conn1commit();
conn2commit();
conn3commit();
//其中只要其中一个执行存储出错,就会跳到catch中,执行rollback,所以不要存储一个就commit一次,要一起commit 。
}catch(Exception e){
conn1rollback();
conn2rollback();
conn3rollback();
}finall{
conn1close();
conn2close();
conn3close();
}
主要就是对于执行成功的存储不要立刻就执行commit *** 作,这样如果之后的数据库出错就无法rollback成功的那个了。
我们等到所以对于数据库的 *** 作都执行完毕后,再一起执行commit,如果其中那个出错也不至于过早的commit,而无法rollback了。
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 delete(int sID) {
dbc = new DataBaseConnection();
Connection con = dbcgetConnection();
try {
consetAutoCommit(false);// 更改JDBC事务的默认提交方式
dbcexecuteUpdate("delete from bylaw where ID=" + sID);
dbcexecuteUpdate("delete from bylaw _content where ID=" + sID);
dbcexecuteUpdate("delete from bylaw _affix where bylawid=" + sID);
concommit();//提交JDBC事务
consetAutoCommit(true);// 恢复JDBC事务的默认提交方式
dbcclose();
return 1;
}
catch (Exception exc) {
conrollBack();//回滚JDBC事务
excprintStackTrace();
dbcclose();
return -1;
}
}
2、SessionBean中的JTA事务
JTA 是事务服务的 J2EE 解决方案。本质上,它是描述事务接口(比如 UserTransaction 接口,开发人员直接使用该接口或者通过 J2EE 容器使用该接口来确保业务逻辑能够可靠地运行)的 J2EE 模型的一部分。JTA 具有的三个主要的接口分别是 UserTransaction 接口、TransactionManager 接口和 Transaction 接口。这些接口共享公共的事务 *** 作,例如 commit() 和 rollback(), 但是也包含特殊的事务 *** 作,例如 suspend(),resume() 和 enlist(),它们只出现在特定的接口上,以便在实现中允许一定程度的访问控制。例如,UserTransaction 能够执行事务划分和基本的事务 *** 作,而 TransactionManager 能够执行上下文管理。
应用程序可以调用UserTransactionbegin()方法开始一个事务,该事务与应用程序正在其中运行的当前线程相关联。底层的事务管理器实际处理线程与事务之间的关联。UserTransactioncommit()方法终止与当前线程关联的事务。UserTransactionrollback()方法将放弃与当前线程关联的当前事务。
public int delete(int sID) {
DataBaseConnection dbc = null;
dbc = new DataBaseConnection();
dbcgetConnection();
UserTransaction transaction = sessionContextgetUserTransaction();//获得JTA事务
try {
transactionbegin(); //开始JTA事务
dbcexecuteUpdate("delete from bylaw where ID=" + sID);
dbcexecuteUpdate("delete from bylaw _content where ID=" + sID);
dbcexecuteUpdate("delete from bylaw _affix where bylawid=" + sID);
transactioncommit(); //提交JTA事务
dbcclose();
return 1;
}
catch (Exception exc) {
try {
transactionrollback();//JTA事务回滚
}
catch (Exception ex) {
//JTA事务回滚出错处理
exprintStackTrace();
}
excprintStackTrace();
dbcclose();
return -1;
}
}
在说他们之间的区别之前,先考虑如下几个问题:
1、getCurrentSession()与openSession()的区别?
采用getCurrentSession()创建的session会绑定到当前线程中,而采用openSession()
创建的session则不会
采用getCurrentSession()创建的session在commit或rollback时会自动关闭,而采用openSession()
创建的session必须手动关闭
2、使用getCurrentSession()需要在hibernatecfgxml文件中加入如下配置:
如果使用的是本地事务(jdbc事务)
<property name="hibernatecurrent_session_context_class">thread</property>
如果使用的是全局事务(jta事务)
<property name="hibernatecurrent_session_context_class">jta</property>
以上是hibernate中一些使用,下面来说说jdbc与jta的区别:
JDBC 事务
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( javasqlConnection )提供了两种事务模式:自动提交和手工提交。
#在jdbc中,事务 *** 作缺省是自动提交。也就是说,一条对数据库的更新表达式代表一项事务 *** 作, *** 作成功后,系统将自动调用commit()来提交,否则将调用rollback()来回滚。
# 在jdbc中,可以通过调用setAutoCommit(false)来禁止自动提交。之后就可以把多个数据库 *** 作的表达式作为一个事务,在 *** 作完成后调 用commit()来进行整体提交,倘若其中一个表达式 *** 作失败,都不会执行到commit(),并且将产生响应的异常;此时就可以在异常捕获时调用 rollback()进行回滚。这样做可以保持多次更新 *** 作后,相关数据的一致性,示例如下:
try {
conn =
DriverManagergetConnection
("jdbc:oracle:thin:@host:1521:SID","username","userpwd";
connsetAutoCommit(false);//禁止自动提交,设置回滚点
stmt = conncreateStatement();
stmtexecuteUpdate(“alter table …”); //数据库更新 *** 作1
stmtexecuteUpdate(“insert into table …”); //数据库更新 *** 作2
conncommit(); //事务提交
}catch(Exception ex) {
exprintStackTrace();
try {
connrollback(); // *** 作不成功则回滚
}catch(Exception e) {
eprintStackTrace();
}
}
JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
JTA事务
JTA(Java Transaction API) 为 J2EE 平台提供了分布式事务服务。
要用 JTA 进行事务界定,应用程序要调用 javaxtransactionUserTransaction 接口中的方法。例如:
utxbegin();
//
DataSource ds = obtainXADataSource();
Connection conn = dsgetConnection();
pstmt = connprepareStatement("UPDATE MOVIES ");
pstmtsetString(1, "Spinal Tap");
pstmtexecuteUpdate();
//
utxcommit();
让我们来关注下面的话:
“用 JTA 界定事务,那么就需要有一个实现 javaxsqlXADataSource 、 javaxsqlXAConnection 和 javaxsqlXAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。”
要使用JTA事务,必须使用XADataSource来产生数据库连接,产生的连接为一个XA连接。
XA连接(javaxsqlXAConnection)和非XA(javasqlConnection)连接的区别在于:XA可以参与JTA的事务,而且不支持自动提交。
注意:
Oracle, Sybase, DB2, SQL Server等大型数据库才支持XA, 支持分布事务。
My SQL 连本地都支持不好,更别说分布事务了。
JTA方式的实现过程:
用XADataSource产生的XAConnection它扩展了一个getXAResource()方法,事务通过这个方法把它加入到事务容器中进行 管理对于调用者来说,根本看不到事务是如果管理的,你只要声明开始事务,告诉容器我下面的 *** 作要求事务参与了,最后告诉事务说到这儿可以提交或回滚了, 别的都是黑箱 *** 作。
在使用JTA之前,你必须首先实现一个Xid类用来标识事务(在普通情况下这将由事务管理程序来处理)。Xid包含三个元素:formatID、gtrid(全局事务标识符)和bqual(分支修饰词标识符)。
下面的例子说明Xid的实现:
import javaxtransactionxa;
public class MyXid implements Xid
{
protected int formatId;
protected byte gtrid[];
protected byte bqual[];
public MyXid()
{
}
public MyXid(int formatId, byte gtrid[], byte bqual[])
{
thisformatId = formatId;
thisgtrid = gtrid;
thisbqual = bqual;
}
public int getFormatId()
{
return formatId;
}
public byte[] getBranchQualifier()
{
return bqual;
}
public byte[] getGlobalTransactionId()
{
return gtrid;
}
}
其次,你需要创建一个你要使用的数据库的数据源:
public DataSource getDataSource()
throws SQLException
{
SQLServerDataSource xaDS = new
commerantdatadirectjdbcxsqlserverSQLServerDataSource();
xaDSsetDataSourceName("SQLServer");
xaDSsetServerName("server");
xaDSsetPortNumber(1433);
xaDSsetSelectMethod("cursor");
return xaDS;
}
例1 这个例子是用“两步提交协议”来提交一个事务分支:
XADataSource xaDS;
XAConnection xaCon;
XAResource xaRes;
Xid xid;
Connection con;
Statement stmt;
int ret;
xaDS = getDataSource();
xaCon = xaDSgetXAConnection("jdbc_user", "jdbc_password");
xaRes = xaCongetXAResource();
con = xaCongetConnection();
stmt = concreateStatement();
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
try {
xaResstart(xid, XAResourceTMNOFLAGS);
stmtexecuteUpdate("insert into test_table values (100)");
xaResend(xid, XAResourceTMSUCCESS);
ret = xaResprepare(xid);
if (ret == XAResourceXA_OK) {
xaRescommit(xid, false);
}
}
catch (XAException e) {
eprintStackTrace();
}
finally {
stmtclose();
conclose();
xaConclose();
}
当然,实际过程中,我们不需要写这些代码,这些代码是JTA最终的实现代码。
关于“两步提交协议”,可以参看下面的文章:
>
以上就是关于JAVA设计模式之事务处理[2]全部的内容,包括:JAVA设计模式之事务处理[2]、java里调用mysql的事务怎么写、用Java编写一般应用程序,怎么实现在一个事务中访问了多种类型数据库。等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)