JDBC、JTA、Spring的事务管理
Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。
这样可以防止出现脏数据,防止数据库数据出现问题。开发中为了避免这种情况一般都会进行事务管理。
JDBC中是通过Connection对象进行事务管理的
Hibernate中是通过Transaction进行事务管理,处理方法与JDBC中类似。
Spring中也有自己的事务管理机制,一般是使用TransactionMananger进行管理,可以通过Spring的注入来完成此功能。
JDBC的事务管理
JDBC的一切行为包括事务是基于一个connection的,在JDBC中是通过Connection对象进行事务管理的,默认是自动提交事务,可以手工将自动提交关闭,通过commit方法进行提交,rollback方法进行回滚,如果不提交,则数据不会真正的插入到数据库中。
基于J2EE分层架构设计思想,数据访问层应该专门处理数据访问,而业务逻辑则在业务逻辑层中处理。为了提高复用,数据访问层对象(DAO)的粒度通常都非常小,一个更新操作被封装在一个方法中,这样当有多个更新操作需要被捆绑为一个事务的时候,事务的处理只能在业务逻辑层中实现。
在不分层的情况下,一个典型的JDBC事务处理代码片断如下。
try {
conn =DriverManager.getConnection
("jdbc:oracle:thin:@host:1521:SID","username","userpwd";
conn.setAutoCommit(false);//禁止自动提交,设置回滚点
stmt = conn.createStatement();
stmt.executeUpdate(“alter table …”); //数据库更新操作1
stmt.executeUpdate(“insert into table …”); //数据库更新操作2
conn.commit(); //事务提交
}catch(Exception ex) {
ex.printStackTrace();
try {
conn.rollback(); //操作不成功则回滚
}catch(Exception e) {
e.printStackTrace();
}
}
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式:自动提交和手工提交。 java.sql.Connection 提供了以下控制事务的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
JDBC的事务管理的安全性及效率分析
如果直接使用基本的JDBC connection,肯定是关掉连接最安全。如果所有代码共用一个连接,会有效率问题,有时还会死锁、connection不是线程安全的。
用不用DataSource跟关不关Connection没直接关系。 用DataSource只是把Connection的创建解离出来,可以变化。 DataSource是个接口,连接池是利用这个接口,返回一个Connection的代理(或者Wrapper,Delegate之类), 在调用这个代理的close方法时,不是直接关闭,而是把Connection放回连接池。
同时,因为需要事务需要保证使用一个连接, 而且如果每次调用 Dao都简单从DataSource建立连接, 不能保证连接一致,效率也会有些问题。 因此,比较好的做法是利用ThreadLocal,调用统一方法建立连接,该方法先检查ThreadLocal里有没有连接,有则利用, 没有则新建,连接放到ThreadLocal里。
Spring的DataSourceUtil是怎么写的,就是那个思路。ThreadLocal不是Connection的封装,而是Connection的保存点。 用连接池并不能保证你用的Connection是按线程一致的。 一般的连接池实现并不关心这点。
使用事务管理代码,在事务开始时,向连接池请求连接, 之后Dao不是直接向连接池请求连接而是获取ThreadLocal中的Connection, 在事务结束后,提交事务,关闭Connection(如果使用连接池,则将Connection返还给事务) 所以必须保证有事务提交和Connection关闭代码。 所以Spring的例子代码,一般是把事务管理放在Service层,而不是Dao层。
JTA的事务管理
Java事务API(JTA;Java Transaction API)J2EE平台提供了分布式事务服务。一个分布式事务(distributed transaction)包括一个事务管理器(transaction manager)和一个或多个资源管理器(resource manager)。一个资源管理器(resource manager)是任意类型的持久化数据存储。事务管理器(transaction manager)承担着所有事务参与单元者的相互通讯的责任。下图显示了事务管理器和资源管理的间的关系。
JTA 事务
JTA (Java Transaction API) 为 J2EE 平台提供了分布式事务服务。
要用 JTA 进行事务界定,应用程序要调用 javax.transaction.UserTransaction 接口中的方法。例如:
import javax.transaction.*;
import javax.naming.*;
// ...
InitialContext ctx = new InitialContext();
Object txObj = ctx.lookup(";java:comp/UserTransaction";);
UserTransaction utx = (UserTransaction) txObj;
应用程序有了UserTransaction对象的引用之后,就可以起动事务。
utx.begin();
// ...
DataSource ds = obtainXADataSource();
Connection conn = ds.getConnection();
pstmt = conn.prepareStatement("UPDATE MOVIES ...");
pstmt.setString(1, "Spinal Tap");
pstmt.executeUpdate();
// ...
utx.commit();
让我们来关注下面的话:
“用 JTA 界定事务,那么就需要有一个实现 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。”
要使用JTA 事务,必须使用XADataSource来产生数据库连接,产生的连接为一个XA连接。
XA连接(javax.sql.XAConnection)和非XA(java.sql.Connection)连接的区别在于:XA可以参与JTA 的事务,而且不支持自动提交。
Note:
Oracle, Sybase, DB2, SQL Server等大型数据库才支持XA, 支持分布事务。
My SQL 连本地都支持不好,更别说分布事务了。
JTA 方式的实现过程 :
用XADataSource产生的XAConnection它扩展了一个getXAResource()方法,事务通过这个方法把它加入到事务容器中进行管理.对于调用者来说,根本看不到事务是如果管理的,你只要声明开始事务,告诉容器我下面的操作要求事务参与了,最后告诉事务说到这儿可以提交或回滚了, 别的都是黑箱操作。
在使用JTA 之前,你必须首先实现一个Xid类用来标识事务(在普通情况下这将由事务管理程序来处理)。Xid包含三个元素:formatID、gtrid(全局事务标识符)和bqual(分支修饰词标识符)。
下面的例子说明Xid的实现:
import javax.transaction.xa.*;
public class MyXid implements Xid
{
protected int formatId;
protected byte gtrid[];
protected byte bqual[];
public MyXid()
{
}
public MyXid(int formatId, byte gtrid[], byte bqual[])
{
this.formatId = formatId;
this.gtrid = gtrid;
this.bqual = bqual;
}
public int getFormatId()
{
return formatId;
}
public byte[] getBranchQualifier()
{
return bqual;
}
public byte[] getGlobalTransactionId()
{
return gtrid;
}
}
其次,你需要创建一个你要使用的数据库的数据源:
public DataSource getDataSource()
throws SQLException
{
SQLServerDataSource xaDS = new
com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource();
xaDS.setDataSourceName("SQLServer");
xaDS.setServerName("server");
xaDS.setPortNumber(1433);
xaDS.setSelectMethod("cursor");
return xaDS;
}
例1?这个例子是用“两步提交协议”来提交一个事务分支:
XADataSource xaDS;
XAConnection xaCon;
XAResource xaRes;
Xid xid;
Connection con;
Statement stmt;
int ret;
xaDS = getDataSource();
xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password");
xaRes = xaCon.getXAResource();
con = xaCon.getConnection();
stmt = con.createStatement();
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
try {
xaRes.start(xid, XAResource.TMNOFLAGS);
stmt.executeUpdate("insert into test_table values (100)");
xaRes.end(xid, XAResource.TMSUCCESS);
ret = xaRes.prepare(xid);
if (ret == XAResource.XA_OK) {
xaRes.commit(xid, false);
}
}
catch (XAException e) {
e.printStackTrace();
}
finally {
stmt.close();
con.close();
xaCon.close();
}
当然,实际过程中,我们不需要写这些代码,这些代码是JTA 最终的实现代码。
关于“两步提交协议”,可以参看下面的文章:
http://www.ibm.com/developerworks/cn/db2/library/techarticles/dm-0505weber/index.html
两阶段提交(Two-Phase-Commit) 协议
首先,两阶段提交(Two-Phase-Commit)事务的启动与常规的单阶段提交(One-Phase-Commit)事务类似。接着,应用程序/客户机对该两阶段提交(Two-Phase-Commit)操作中所涉及的所有数据库执行其修改工作。现在,在最终提交该事务之前,客户机通知参与的数据库准备 提交(第 1 阶段)。如果客户机从数据库收到一条“okay”,就发出命令向数据库提交该事务(第 2 阶段)。最后分布式事务(Distributed Transaction)结束。
本文中的例子演示了如何在 Java 中使用 JTA 实现两阶段提交(Two-Phase-Commit)协议。在该应用程序中,如果一个事务分支报告了错误,您就要负责进行错误处理。但是“两阶段提交协议 简介”小节中提到仍然存在一个问题,那就是如果第 2 阶段中一个事务分支发生故障,该怎么办呢?
如果再次查看程序代码,您可以看到在“第 1 阶段”和“第 2 阶段”之间有一个很小的时间间隔。在这一时间间隔中,出于某种理由,其中某一参与数据库可能崩溃。如果发生了,我们将陷入分布式事务已经部分提交的情形中。
假 定下列情形:在“第 1 阶段”之后,您从 DB2 和 IDS 数据库中都收到了“okay”。在下一步中,应用程序成功提交了 DB2 的事务分支。接着,应用程序通知 DB2 事务分支提交事务。现在,在应用程序可以通知 IDS 事务分支提交它这一部分之前,IDS 引擎由于断电发生崩溃。这就是一种部分提交全局事务的情形。您现在该怎么办呢?
在重启之后,DB2 和 IDS 都将尝试恢复打开的事务分支。该引擎等待来自应用程序的提示如何做。如果应用程序没有准备重新发送“第 2 阶段”的提交,该事务分支将被引擎所启动的试探性回滚中止。这是非常糟糕的,因为这将使该全局事务处于不一致状态。
一种解决方案是用一个小型应用程序连接引擎中打开的事务分支,并通知引擎提交或回滚这一打开的事务。如果您使用 IDS 作为后端,那么还有一个隐藏的 onmode 标志,允许您结束打开的事务分支。(onmode -Z xid )。
在 DB2 UDB 中,您可以发出 LIST INDOUBT TRANSACTIONS 来获得打开的 XA 事务的有关信息。您必须查看 DB2 Information Center 中的描述来解决该问题。
上面描述的情形是一个很好的例子,也是使用应用程序服务器(Application Server)或事务监控器(Transaction Monitor)的理由。在使用一个中间层服务器时,就由该服务器负责保持事情正常。
JDBC、JTA、Spring的事务管理的更多相关文章
- Spring的事务管理
事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...
- spring,mybatis事务管理配置与@Transactional注解使用[转]
spring,mybatis事务管理配置与@Transactional注解使用[转] spring,mybatis事务管理配置与@Transactional注解使用 概述事务管理对于企业应用来说是至关 ...
- Hibernate与Spring的事务管理
什么是事务 这个问题比较大,按照我的理解就是,一个事务内的n个操作,要么全部完成,一旦有一个操作有问题,那么所有的操作都全部回滚. Jdbc的事务 首先,大家已经知道了,事务说白了就是一个词----统 ...
- Spring Boot事务管理(上)
摘要 本文主要介绍基于Spring Boot的事务管理,尤其是@Transactional注解详细用法.首先,简要介绍Spring Boot中如何开启事务管理:其次,介绍在Spring,Spring ...
- Spring 对事务管理的支持
1.Spring对事务管理的支持 Spring为事务管理提供了一致的编程模板,在高层次建立了统一的事务抽象.也就是说,不管选择Spring JDBC.Hibernate .JPA 还是iBatis,S ...
- Spring Boot事务管理(中)
在上一篇 Spring Boot事务管理(上)的基础上介绍Spring Boot事务属性和事务回滚规则 . 4 Spring Boot事务属性 什么是事务属性呢?事务属性可以理解成事务的一些基本配置, ...
- spring,mybatis事务管理配置与@Transactional注解使用
spring,mybatis事务管理配置与@Transactional注解使用[转] spring,mybatis事务管理配置与@Transactional注解使用 概述事务管理对于企业应用来说是 ...
- 【Spring Boot学习之四】Spring Boot事务管理
环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 一.springboot整合事务事务分类:编程事务.声明事务(XML.注解),推荐使用注解方式,springboot默 ...
- 关于spring中事务管理的几件小事
1.Spring中的事务管理 作为企业级应用程序框架,Spring在不同的事务管理API之上定义了一个抽象层.而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制. S ...
- 事务以及Spring的事务管理
一.什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行 二.事务的特性(ACID) 原子性: 事务是最小的执行单位,不允许分割.事务的原子性确保动作要么全部完成,要么完全不起作用: 一致性 ...
随机推荐
- 探索ASP.NET MVC框架之路由系统
引言 对于ASP.NET MVC的路由系统相信大家肯定不陌生.今天我们就深入ASP.NET的框架内部来看一下路由系统到底是怎么通过我们给出的地址(例如:/Home/Index)解析出Controlle ...
- yaf学习资料
yaf学习资料 文档 鸟哥的官方文档 Yaf框架结合PHPUnit的集成测试 php yaf框架扩展实践六--单元测试.计划任务.第三方库等 php yaf框架扩展实践一--配置篇 yaf实战例子 y ...
- esnext:最后一个参数后面也允许加逗号了
https://jeffmo.github.io/es-trailing-function-commas 目前是一个 stage 3 的提案,Chakra 和 JSC 已经实现了,它允许我们在函数定义 ...
- 表单提交按钮input和button、a的差异
现在普遍的在网页中,表单提交数据的按钮最常见实用有三种,一种是input,一种是button,最后一种,是其他如a标签,div标签,span标签代替而来.在以前的日子里,大家都习惯于用input,因为 ...
- Coursera系列-R Programming第二周
博客总目录,记录学习R与数据分析的一切:http://www.cnblogs.com/weibaar/p/4507801.html --- 好久没发博客 且容我大吼一句 终于做完这周R Progra ...
- PHP之static静态变量详解(二)
在看别人项目过程中,看到函数里面很多static修饰的变量,关于static修饰的变量,作用域,用法越看越困惑,所以查了下资料. static用法如下: 1.static 放在函数内部修饰变量 2.s ...
- [BZOJ3262]陌上花开
[BZOJ3262]陌上花开 试题描述 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一 ...
- Python递归及斐波那契数列
递归函数 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数.举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 fact(n)表示,可 ...
- No Entity Framework provider found for the ADO.NET provider with invariant
在使用EF的时候,我把EF的EDMX放在单独的一个project中,UI中引用这个project的dll, 运行的时候就是提示No Entity Framework provider found fo ...
- Apache与Nginx对客户端请求的处理机制对比
Apache与Nginx对客户端请求的处理机制对比 模块 大致为四个模块,核心模块.HTTP模块.邮件模块,以及第三方模块 核心模块主要包含两类功能的支持,一类是主体功能,包括进程管理,权限管理,错误 ...