JavaEE5学习笔记01-JTA和数据库事务
Email:suhuanzheng7784877@163
刘岩
1. 前言
JavaEE5的标准已经不是新鲜东西了,现在出了JavaEE6的标准,不过因为应用服务器像Weblogic和Websphere还没有大力支持呢,所以暂时先放放。
JavaEE5是以EJB3.0为核心内容所出的标准。
主要有:
Servlet2.5
JSP2.1
JSF1.2
JSTL1.2
JTA1.1
JPA2.0
EJB3.0
JMS1.1
JavaMail1.4
以上就是JavaEE5提出的相关标准
因为JPA在笔者的博客中单独有总结,在此系列不再做出总结,至于JSF等Web前端技术,之后会做一个文档形式的总结的。笔者是以Jboss5作为应用服务器(尽管JBoss6已经出了,并且支持JavaEE6标准),JDK版本是1.6.22,开发IDE为MyEclipse8.6.1。
2. JTA简介
JTA(Java Transaction API)提供了事务划分接口标准,比如同一个数据库的事务,跨数据库访问业务的全局事务。在Java Web当中可能事务并不是十分重要(相对来说),而在JavaEE企业级开发,比如涉及到金额的电信、银行、商务等等对事务比较敏感的系统中,那么事务控制就是一个重点中的重点了。
JavaEE通常分为局部事务(Local Transaction Processing)和全局事务(Distributed Transaction Processing)。如果业务是单一数据源就是用局部事务就可以了,如果需要多个数据源就需要全局事务来保证对外是透明的、就跟一个数据源没两样的策略。
3. 简单的JTA例子
比如说我的数据库记录目前记录如下
Id是主键
之后我们在JBOSS中配置2个XA类型的数据源同时指向这一个数据库。
oracle1数据源配置内容如下
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<datasources>
    <xa-datasource>
      <jndi-name>oracle1</jndi-name>
        <rar-name>jboss-xa-jdbc.rar</rar-name>
        <use-java-context>true</use-java-context>
        <connection-definition>javax.sql.DataSource</connection-definition>
        <jmx-invoker-name>jboss:service=invoker,type=jrmp</jmx-invoker-name>
        <min-pool-size>0</min-pool-size>
        <max-pool-size>10</max-pool-size>
        <blocking-timeout-millis>30000</blocking-timeout-millis>
        <idle-timeout-minutes>30</idle-timeout-minutes>
        <prefill>false</prefill>
        <background-validation>false</background-validation>
        <background-validation-millis>0</background-validation-millis>
        <validate-on-match>true</validate-on-match>
        <no-tx-separate-pools/>
        <statistics-formatter>source.statistic.pool.JBossDefaultSubPoolStatisticFormatter</statistics-formatter>
        <isSameRM-override-value>false</isSameRM-override-value>
        <allocation-retry>0</allocation-retry>
        <allocation-retry-wait-millis>5000</allocation-retry-wait-millis>
        <application-managed-security xsi:type="securityMetaData" xmlns:xsi="/2001/XMLSchema-instance"/>
        <metadata>
            <type-mapping>Oracle9i</type-mapping>
        </metadata>
        <type-mapping>Oracle9i</type-mapping>
        <user-name>system</user-name>
        <password>111111</password>
        <exception-sorter-class-name>source.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
        <prepared-statement-cache-size>0</prepared-statement-cache-size>
        <share-prepared-statements>false</share-prepared-statements>
        <set-tx-query-timeout>false</set-tx-query-timeout>
        <query-timeout>0</query-timeout>
        <use-try-lock>60000</use-try-lock>
        <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
        <xa-datasource-property name="URL">jdbc:oracle:thin:@127.0.0.1:1521:jbossdemooracle</xa-datasource-property>
        <xa-resource-timeout>0</xa-resource-timeout>
    </xa-datasource>
</datasources>
oracle2内容和它差不多,只是jndi-name不同而已。
下面是未加事务控制的测试代码
        try {
            Context ctx = new InitialContext();
            DataSource ds1 = (DataSource) ctx.lookup("java:/oracle1");
            DataSource ds2 = (DataSource) ctx.lookup("java:/oracle2");
           
            Connection connection1 = Connection();
            Connection connection2 = Connection();
           
           
            Statement statement1 = ateStatement();
            Statement statement2 = ateStatement();
           
           
            String sql1 = "insert into jimmy_user values(3,'青阳子')";
            String sql2 = "insert into jimmy_user values(2,'非凡公子')";
           
           
            int sun1 = uteUpdate(sql1);
            int sun2 = uteUpdate(sql2);
           
           
            statement1.close();
            connection1.close();
           
            statement2.close();
            connection2.close();
        } catch (NamingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
执行后看到数据库的结果如下:
青阳子插进去了,非凡公子因为主键重复被甩了!
下面我们再看加了JTA处理后的代码如下
        UserTransaction userTransaction = null;
        try {
            Context ctx = new InitialContext();
            DataSource ds1 = (DataSource) ctx.lookup("java:/oracle1");
            DataSource ds2 = (DataSource) ctx.lookup("java:/oracle2");
            userTransaction = (UserTransaction) ctx.lookup("UserTransaction");
            Connection connection1 = Connection();
            Connection connection2 = Connection();
            userTransaction.begin();
            Statement statement1 = ateStatement();
            Statement statement2 = ateStatement();
            String sql1 = "insert into jimmy_user values(4,'谈无欲')";
            String sql2 = "insert into jimmy_user values(3,'傲笑红尘')";
            int sun1 = uteUpdate(sql1);
            int sun2 = uteUpdate(sql2);
            statement1.close();
            connection1.close();
            statement2.close();
            connection2.close();
        } catch (NamingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NotSupportedException e) {
            // TODO Auto-generated catch block
java库
            e.printStackTrace();
        } catch (SystemException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            try {
                llback();
            } catch (IllegalStateException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (SecurityException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (SystemException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
执行之后,“谈无欲”与“傲笑红尘”都没进入数据库,全局事务回滚了。
4. XA规范的全局事务中间件
XA是分布式事务处理的规范,具体的实现是不同的数据库厂商自己实现的,mysql驱动目前不支持XA规范。
通常情况下,交易中间件与数据库通过XA 接口规范,使用两阶段提交来完成一个全局事务,XA规范的基础是两阶段提交协议。
在第一阶段,交易中间件请求所有相关数据库准备提交(预提交)各自的事务分支,以确认是否所有相关数据库都可以提交各自的事务分支。当某一数据库收到预提交后,如果可以提交属于自己的事务分支,则将自己在该事务分支中所做的操作固定记录下来,并给交易中间件一个同意提交的应答,此时数据库将不能再在该事务分支中加入任何操作,但此时数据库并没有真正提交该事务,数据库对共享资源的操作还未释放(处于上锁状态)。如果由于某种原因数据库无法提交属于自己的事务分支,它将回滚自己的所有操作,释放对共享资源上的锁,并返回给交易中间件失败应答。
在第二阶段,交易中间件审查所有数据库返回的预提交结果,如所有数据库都可以提交,交易中间件将要求所有数据库做正式提交,这样该全局事务被提交。而如果有任一数据库预提交返回失败,交易中间件将要求所有其它数据库回滚其操作,这样该全局事务被回滚。
以一个全局事务为例,AP首先通知交易中间件开始一个全局事务,交易中间件通过XA接口函数通知数据库开始事务,然后AP可以对数据库管理的资源进行操作,数据库系统记录事务对本地资源的所有操作。操作完成后交易中间件通过XA接口函数通知数据库操作完成。交易中间件负责记录AP操作过哪些数据库(事务分支)。AP根据情况通知交易中间件提交该全局事务,交易中间件会通过XA接口函数要求各个数据库做预提交,所有数据库返回成功后要求各个数据库做正式提交,此时一笔全局事务结束。XA规范对应用来说,最大好处在于事务的完整性由交易中间件和数据库通过XA接口控制,AP只需要关注与数据库的应用逻辑的处理,而无需过多关心事务的完整性,应用设计开发会简化很多。具体来说,如果没有交易中间件,应用系统需要在程序内部直接通知数据库开始、结束和提交事务,当出现异常情况时必须由专门的程序对数据库进行反向操作才能完成回滚。如果是有很多事务分支的全局事务,回滚时情况将变得异常复杂。而使用XA接口,则全局事务的提交是由交易中间件控制,应用程序只需通知交易中间件提交或回滚事务,就可以控制整个事务(可能涉及多个异地的数据库)的全部提交或回滚。
Jboss就好似这种XA中间件,用户只要在代码中显示的利用JTA接口开启、提交事务,调用预先配置好的XA数据源就可以了,底层如何做的对用户是透明的。Jboss为你做了这一切,尽管全局性事务执行速度上有些慢……

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。