JDBC分布式事务: 在没有开始JDBC分布式事务前,先来回顾一下J2EE平台的数据源的整体构加: 在上面的介绍中,可能大家会过于限入理解如何实现ConnectionPoolDataSource和PooledConnection,而忘记了它的整 体结构.为了帮助大家更快地理解,我从以下几点进行总结. 声明:本文完全是作者根据SUN的文档从实践中总结,没有参考(事实上目前我还没有找到这方面的参考)任何文章:
1. DataSource和ConnectionPoolDataSource,PooledConnection,Connection的关系: Sun的文档中只对ConnectionPoolDataSource接口作了一般性规定,说明它是PooledConnection的工厂,即 ConnectionPoolDataSource是传统的连结池角色,它负责产生物理连结PooledConnection.而 PooledConnection又是Connectio的工厂,一个PooledConnection对象负责产生多个Connection对象供应用 程序调用.而DataSource是对上面两个过程的包装,在DataSource中不仅要实现传统连结池 ConnectionPoolDataSource来产生物理连结PooledConnection,还要实现通过每个PooledConnection 工厂来产生Connection,最后DataSource通过公开方法返回给调用者的是经过两次工厂出来的Connection.如果我们先不考虑分布 式事务,只看下图左边,就是说工厂ConnectionPoolDataSource生成PooledConntion,二级工厂 PooledConntion生产Connection,这两个过程由DataSource在内部包装,只提供最后的产品Connection.
2. DataSource是服务端数据源,而传统的连结池是客户端数据源: 传统的连结池要调用者生成这个连池的实例,完成初如化,这样一个数据库为了防止连结池的实例生成无限多个物理连结,就要对保存物理连结的数据结构进行静态 定义,否则,你在你的程序中生成一个连结池对象,它生成30个物理连结,我又在我的程序中生成连结池实例,又生成30个物理连结,那就无法控制了,所以保 存这30个物理连结的数据结构必须是静态的. 而DataSource同一个对象初始化后,对象被绑定到jdni服务器上,通过jdni得到的是它的代码存根,其中只包含Connection,而物理 连结是不可能序列化的,所以不会被重新生成,调用者通过Connection对象作为参数传给服务端,由它来操作实际的物理连结. 思考一下:如果不考虑性能问题,我是否可以把PooledConnction不再二次工厂化,只把PooledConntion作为Connection 作为DataSource产品返回给调用者? 答案是不可以,因为物理连结不能序列化,也就是无法进行分布式引用.二次工厂化不仅解决了性能的问题,也同时解决了分布式调用的问题.
3. 为什么说二次工厂化增加性能? 对于产生物理连结,没有什么区别,但物理连结本身并没有满负载工作,也就是一个物理Connection(TMD,我现在也不好说Connection还 是PooledConnectio,以前的Connection就是DataSource中的PooledConnection)其实可以同时绑定更多的 Statement,而如果它直接给调用者调用了,句柄就被调用者拿去了,在调用者没有返回时别的Statement没法和它进行”联系”.而二次工厂的 目的就是把多人的Statement通过”新的引用Connection”和物理连结绑定,使它更好地工作. 举个例子,汽车这种东西,在目前的中国还是很昂贵的,作为客户(调用者)我有几件货物要运,但一辆汽车(物理连结)如果我一用,别人就不能用了.(传统连 结池和连结),尽管它还可以装更多的货物,现在汽车公司只能你一个车号(新的Connection),不给你实际的汽车,你只要把你的几件东西只交给这个 车号,而其他人也可能同时把几件东西交给这个车号,最尽有更多的货物因为使用同一车号而使那个物理汽车装载了更多的货物,当然如果它满了的话会产生另一辆 车,如果生产的辆达到规定的数目你只好等等了,但这样把多个客户的货物和同一车号关联使汽车能更多地处理事务,明显地增加了性能. 理解了以上的结构,我们就不难理解javax.sql对分布式事务的支持,当然,如果你对事务本身还不理解,那我就没办法让你理解以下的知识,因为我不可 能再停下来讲什么是事务.它是和JDBC相同级别的内容,也许在别的地方我会再讲. 从上面的结构中右边看到,在DataSource中,其实封装了两种工厂,这两种工厂都是两层次的,其实XADataSource的作用和 ConnectioPooledDataSource一样,都是产生物理连结的,只不过它产生支持分布式事务的物理连结XAConnectio而已,(以 后记住,凡以XA命名的类都是支持分布式事务的标记.)我们看到,XAConnection中getConnection()出来的连结和 PooledConnection中getConnection()出来的连结没有区别,而Connection是DataSource的最终产品,这意 味作什么? 这意味着支持分布式事务的过程由DataSource来做,你要操作的Connection和平时没有两样,你只要声明事务的开始和事务提交就行了! 要使你的连结支持分布式事务,你要在DataSource的配置中指明type是XADataSource就行了.然后申请一个一务(为了说明方便省略了 try{}catch(){}) UserTransaction ut = ...........; ut.begin(); Connection con1 = .........; Connection con2 = .........; Connection con3 = .........; if(条件) ut.setRollbackOnly(); con1.close(); con2.close(); con3.close(); 考察一下,为什么XADataSource类型的物理工厂会产生的连结可以直接被事务管理呢?其实这就是封装的好处了,在XADataSource产生 XAConnection时,这个XAConnection实际是PooledConnection的子类,它扩展了一个getXAResource() 方法,事务通过这个方法把它加入到事务容器中进行管理.对于调用者来说,根本看不到事务是如果管理的,你只要声明开始事务,告诉容器我下面的操作要求事务 参与了,最后告诉事务说到这儿可以提交或回滚了,别的都是黑箱操作,不要你来做. 当然如果没有分布式事务的需求,虽然XADataSource可以用于本地事务,但它要做很多资源测试,是一种浪费. 最后要说明的是,既然你把操作交给事务来做,你就要对他放心,事务边界由容器管理,你只在最后确定是提交还是回滚还是强行回滚 setRollbackOnly()(强行回滚后不可以再提交).
你不要在事务中调用某一连结的rollback,commit,也不能把 Connection设为自动提交,一般来说当你声明为支持分布式事务的DataSource时,创建的连结默认都是关闭自动提交的,只是你自己不要打开 它. 因为SUN的文档只对DataSource接口作了一般规定,并没有规定具体算法,所以我们在清楚上面的结构后,可以实现不依赖容器的 DataSource(其实只是它的思想.因为你写出来的不依赖容器的DataSource)已经不是这个意义上的DataSource了.它不能绑定到 服务器上让远程引用,所以生成物理连结的工厂应该是静态的,而物理连结这种产品也应该是静态的.然后再生成多个引用连结.但这好象没有多大意义,因为纯客 户端软件一般来说不可能同时有上万个客户在线访问的,根本用不着这么费事地实现连结池.