JTA事务

作者: 米刀灵 | 来源:发表于2017-08-29 12:53 被阅读263次

JDBC和JTA事务区别
简单的说 jta是多库的事务 jdbc是单库的事务。

jdbc事务
JDBC事务由Connnection对象控制管理,也就是说,事务管理实际上是在JDBC Connection中实现。事务周期限于Connection的生命周期。java.sql.Connection提供了两种事务模式:自动提交和手工提交。

自动提交:缺省是自动提交。一条对数据库的更新(增/删/改)代表一项事务操作,操作成功后,系统将自动调用commit()来提交,否则将调用rollback()来回滚。
手工提交:通过调用setAutoCommit(false)来禁止自动提交。这样就可把多个数据库操作的表达式作为一个事务,在操作完成后调用commit()来进行整体提交,其中任何一个操作失败,都不会执行到commit(),并产生异常;此时可在异常捕获时调用rollback()进行回滚,以保持多次更新操作后,相关数据的一致性,示例如下:

    try {
        conn =DriverManager.getConnection(...);
        conn.setAutoCommit(false);//禁止自动提交,设置回滚点
        stmt = conn.createStatement();
        stmt.executeUpdate(...); //数据库更新操作1
        stmt.executeUpdate(...); //数据库更新操作2
        conn.commit(); //事务提交
    }catch(Exception ex) {
        log.error(...);
        try {
            conn.rollback(); //操作不成功则回滚
        }catch(Exception e) {
            log.error(...);
        }
    }

JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个JDBC事务不能跨越多个数据库。也无法在通过RPC的方式调用中保证事务。

jta事务
由于JDBC无法实现分布式事务。例如操作不同的数据库或MQ(MQ也可以认为是一个数据源),这时候就无法使用JDBC来管理事务:

    /** 支付订单处理 **/
    @Transactional(rollbackFor = Exception.class)
    public void completeOrder() {
        orderDao.update(); // 订单服务本地更新订单状态
        accountService.update(); // 调用资金账户服务给资金帐户加款
        pointService.update(); // 调用积分服务给积分帐户增加积分
        accountingService.insert(); // 调用会计服务向会计系统写入会计原始凭证
        merchantNotifyService.notify(); // 调用商户通知服务向商户发送支付结果通知
    }

其中调用了五个服务,这五个服务都通过RPC的方式调用。虽然方法中增加了@Transactional注解,但是由于采用调用了分布式服务,该事务并不能达到ACID的效果。
JTA(Java Transaction API)提供了跨数据库连接(或其他JTA资源)的事务管理能力。JTA事务管理则由JTA容器实现。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:JDBC连接、JDO PersistenceManager 对象、JMS 队列、JMS 主题、企业JavaBeans(EJB)等。

Spring+Atomikos实现JTA:

添加依赖:

  <!-- jta -->
  <dependency>
      <groupId>com.atomikos</groupId>
      <artifactId>transactions-jdbc</artifactId>
      <version>4.0.4</version>
  </dependency>
  <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.6.11</version>
  </dependency>

配置文件applicationContext-jta.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

        <bean id="dataSource_main" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
            <property name="uniqueResourceName" value="mysql/main" /><!-- 取名任意,但不要重复-- >
            <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
            <property name="xaProperties">
                <props>
                    <prop key="user">yourusername</prop>
                    <prop key="password">yourpswd</prop>
                    <prop key="URL">jdbc:mysql://yourdatabasehost/tmg</prop>
                </props>
            </property>
            <property name="minPoolSize" value="1" />
            <property name="maxPoolSize" value="3" />
            <property name="maxIdleTime" value="60" />
        </bean>

        <bean id="dataSource_other" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
            <property name="uniqueResourceName" value="mysql/other" /><!-- 取名任意,但不要重复-- >
            <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
            <property name="xaProperties">
                <props>
                    <prop key="user">yourusername</prop>
                    <prop key="password">yourpswd</prop>
                    <prop key="URL">jdbc:mysql://yourdatabasehost/tmg</prop>
                </props>
            </property>
            <property name="minPoolSize" value="1" />
            <property name="maxPoolSize" value="3" />
            <property name="maxIdleTime" value="60" />
        </bean>


        <!-- 配置mybitasSqlSessionFactoryBean -->
        <bean id="sqlSessionFactory_main" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource_main" />
            <property name="configLocation" value="classpath:config/mybatis.xml"/>
            <property name="mapperLocations" value="classpath*:mybatis.mappers.main.*/*.xml" />
        </bean>
        <bean id="sqlSessionFactory_other" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource_other" />
            <property name="configLocation" value="classpath:config/mybatis.xml"/>
            <property name="mapperLocations" value="classpath*:mybatis.mappers.other.*/*.xml" />
        </bean>

        <!-- 配置SqlSessionTemplate -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.tmg.dao.main" />
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_main" />
        </bean>
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.tmg.dao.other" />
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_other" />
        </bean>

        <!-- 下面三个Bean可以独立使用来进行事务控制 -->
        <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
            <property name="forceShutdown" value="true" />
        </bean>

        <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
            <property name="transactionTimeout" value="3000" />
        </bean>

        <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager" ref="atomikosTransactionManager"/>
            <property name="userTransaction" ref="atomikosUserTransaction"/>
        </bean>

        <aop:config proxy-target-class="true">
            <aop:advisor pointcut="execution(* *com.tmg.service..*(..))" advice-ref="txAdvice" />
        </aop:config>

        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
                <tx:method name="save*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="delete*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="del*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="update*"  propagation="REQUIRED"  read-only="true" />
            </tx:attributes>
        </tx:advice>

    </beans>

Dao层:

Service层:

    @Override
    public void addAndDel(MybtUser user) {
        userMapper_main.insert(user);
        userMapper_other.deleteByPrimaryKey(user.getId());
    }

测试:

    @Test
    public void testJta() throws Exception{
        MybtUser user = new MybtUser(5,3,"E");
        userService.addAndDel(user);
    }

把MQ也加入JTA事务:

<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="messageListener" ref="jmsQueueReceiver" />
    <property name="destination" ref="queueDestination" />
    <property name="sessionTransacted" value="true"/>
    <property name="transactionManager" ref="transactionManager"/>
</bean>

这样消息监听器进行消息接收和对应的数据库访问就会处于同一数据库控制下,当消息接收失败或数据库访问失败都会进行事务回滚操作。当指定了transactionManager时,消息监听容器将忽略sessionTransacted的值。

相关文章

  • JTA事务

    JDBC和JTA事务区别简单的说 jta是多库的事务 jdbc是单库的事务。 jdbc事务JDBC事务由Connn...

  • [七]分布式事务

    JTA实现分布式事务 使用Spring boot JTA 可以使用如Jboss之类的应用服务器提供的JTA事务管...

  • java事务

    1、java事务介绍 2、JDBC事务 3、JTA事务 1、java事务介绍 java事务分类:JDBC事务、...

  • 编辑 Java 中的事务 — JDBC 事务和 JTA 事务

    Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。 常见的...

  • 分布式事务-3

    JTA/JTS Java Transaction API Java事务API(Java Transaction A...

  • 分布式事务实现-TCC模式

    事务管理的过程 do commit/rollback JTA事务管理的过程 do prepare/rollback...

  • SpringBoot整合JTA

    导读 JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络...

  • SpringBoot整合JTA事务管理

    JTA TranscationManager XARescource XID 缺点 两阶段提交 事务时间太长 锁数...

  • spring事务管理那些事

    一、spring事务介绍 spring事务优点 对不同的api进行统一编程模型,如JTA,JDBC,Hiberna...

  • ATOMIKOS+JTA分布式事务记录

    ATOMIKOS+JTA是用来分布式事务的中间件,那么什么是分布式事务呢? 事务,分为单机事务,分布式事务;单机事...

网友评论

      本文标题:JTA事务

      本文链接:https://www.haomeiwen.com/subject/srsodxtx.html