【问题标题】:springboot jta transaction rollback is not workingspringboot jta事务回滚不起作用
【发布时间】:2017-05-30 14:18:36
【问题描述】:

我想使用 JTA(使用 atomikos)配置跨数据库执行事务。

我有下面的代码,我想在一个事务中执行。但是,当我运行应用程序时,它会保存 entityObject1 并更新 eventObject2,并且在我运行 l.intValue() 语句时抛出异常时不会回滚。以下是我在 JTA 配置中使用的所有代码。

我错过了什么吗?谁能帮忙。

public void testJTATRansaction() {
    service1.saveEvent1(eventObject1);
    service2.updateEvent2(eventObject2);
}           

service1 中的 saveEvent1 方法:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor = Exception.class)
public int saveEvent1(Object eventObject1) {
    return repository1.save(eventObject1);
}

service2 中的updateEvent2 方法:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor = Exception.class)
public int updateEvent2(Object eventObject2) {
    int i = l.intValue();   //l is null object, to throw error
    return repository2.updateEvent2(eventObject2);
}

我正在使用 repository1 中的默认保存方法(JPARepository 保存方法)。

repository2 类中的 updateEvent2 方法:

@Modifying
@Transactional(propagation=Propagation.REQUIRED, rollbackFor = Exception.class)
@Query(UPDATE_EVENTS)
public int updateEvent2(
          @Param(value = "eventObject2") Object eventObject2);

我正在使用 Spring Boot 应用程序类来初始化我的应用程序:

@SpringBootApplication
@ComponentScan("com.cbc.event")
@EnableTransactionManagement
public class RatingDaemonApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(RatingDaemonApplication.class);
    }
}

我有以下 JTA 配置:

@Configuration
@ComponentScan
@EnableTransactionManagement
public class JTATransactionConfig {

@Bean
public JpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
    hibernateJpaVendorAdapter.setShowSql(true);
    hibernateJpaVendorAdapter.setGenerateDdl(true);
    hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
    return hibernateJpaVendorAdapter;
}

@Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable {
    UserTransactionImp userTransactionImp = new UserTransactionImp();
    userTransactionImp.setTransactionTimeout(10000);
    return userTransactionImp;
}

@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable {
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    userTransactionManager.setForceShutdown(false);

    AtomikosJtaPlatform.transactionManager = userTransactionManager;

    return userTransactionManager;
}

@Bean(name = "transactionManager")
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable {
    UserTransaction userTransaction = userTransaction();

    AtomikosJtaPlatform.transaction = userTransaction;

    TransactionManager atomikosTransactionManager = atomikosTransactionManager();
    return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}

}

数据源配置为:

@Configuration
@DependsOn("transactionManager")
@PropertySource({"classpath:application.properties"})
@EnableJpaRepositories(basePackages = {"com.cbc.repository"}, 
    transactionManagerRef="transactionManager", entityManagerFactoryRef = "entityMF")
public class dataSourceConfiguration {

    @Autowired
    Environment env;

    @Autowired
    JpaVendorAdapter jpaVendorAdapter;

    public DataSource eventsDS() {

        AtomikosDataSourceBean xaDS = new AtomikosDataSourceBean();
        xaDS.setXaDataSourceClassName(env.getProperty(DRIVER_CLASS_NAME));
        xaDS.setXaDataSource(getMysqlXADataSource());       
        xaDS.setUniqueResourceName("DS");
        xaDS.setMaxPoolSize(3);
        return xaDS;
    }

    private MysqlXADataSource getMysqlXADataSource() {
        MysqlXADataSource ds = new MysqlXADataSource();
        ds.setPinGlobalTxToPhysicalConnection(true);
        ds.setURL(env.getProperty(URL));
        ds.setUser(env.getProperty(USER));
        ds.setPassword(env.getProperty(PASSWORD));
        return ds;
    }

    @Bean(name="entityMF")
    public LocalContainerEntityManagerFactoryBean importedEventsEntityMF() {
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
        properties.put("javax.persistence.transactionType", "JTA");

        LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
        entityManager.setJtaDataSource(eventsDS());
        entityManager.setJpaVendorAdapter(jpaVendorAdapter);
        entityManager.setPackagesToScan("com.cbc.events");
        entityManager.setPersistenceUnitName("persistenceUnit");
        entityManager.setJpaPropertyMap(properties);
        return entityManager;
    }
}

我有下面的 AtomikosJtaPlatform 类

public class AtomikosJtaPlatform extends AbstractJtaPlatform {

    private static final long serialVersionUID = 1L;

    static TransactionManager transactionManager;
    static UserTransaction transaction;

    @Override
    protected TransactionManager locateTransactionManager() {
        return transactionManager;
    }

    @Override
    protected UserTransaction locateUserTransaction() {
        return transaction;
    }
}

【问题讨论】:

  • 两个服务在事务范围外调用后抛出异常。
  • 抱歉,我的错误,已编辑问题。 service2 类的 updateEvent2 方法中抛出异常。还是同样的问题。我看到 service1 方法插入了一条记录,即使 service2 方法在事务期间抛出异常。
  • 好吧,现在您有 2 笔交易。它们看起来应该在 service2.update 回滚时提交第一个。你会看到插入的记录。
  • @AndriySlobodyanyk 但我已经提到了 @Transactional(propagation=Propagation.REQUIRED, rollbackFor = Exception.class) 所以两者都应该在同一个事务中。对于信息,这两个事务都发生在不同的数据库中(这就是我使用 Atomikos 的 XA 事务的原因)。 AFAI 知道我是否使用propagation=Propagation.REQUIRED,那么在执行该方法时应该使用相同的事务。
  • 事务边界是被注释的方法。将两个事务粘合在一起的唯一方法是在 testJTATRansaction 上使用 @Transactional。我想这是 Spring 托管测试,因为您在其中有自动装配服务,不是吗?顺便说一句,Propagation.REQUIREAD 是默认值,而 rollbackFor 属性仅适用于已检查的异常,无论如何都提到了包括 NPE 在内的运行时异常。为了确定在后台做什么,打开 org.springframework.transactional 的调试日志

标签: spring-boot spring-data jta


【解决方案1】:

这是来自 Spring 文档

当传播设置为 PROPAGATION_REQUIRED 时,将为应用该设置的每个方法创建一个逻辑事务范围。每个这样的逻辑事务范围都可以单独确定仅回滚状态,外部事务范围在逻辑上独立于内部事务范围。当然,在标准 PROPAGATION_REQUIRED 行为的情况下,所有这些范围都将映射到同一个物理事务。因此,在内部事务范围内设置的仅回滚标记确实会影响外部事务实际提交的机会(正如您所期望的那样)。

但是,在内部事务范围设置了仅回滚标记的情况下,外部事务尚未决定回滚本身,因此回滚(由内部事务范围静默触发)是意外的。此时会引发相应的 UnexpectedRollbackException。这是预期的行为,因此事务的调用者永远不会被误导以为执行了提交,而实际上并没有执行。 因此,如果内部事务(外部调用者不知道)默默地将事务标记为仅回滚,外部调用者仍会调用 commit。外部调用者需要接收 UnexpectedRollbackException 以清楚地表明执行了回滚。

Transaction propagation

尝试如下更改方法声明并试一试

public int saveEvent1(Object eventObject1) 抛出 UnexpectedRollbackException

public int updateEvent2(Object eventObject2) 抛出 UnexpectedRollbackException

为避免此类事情,最好在其中一个服务类或完全不同的服务类中使用单独的方法,并使用事务注释一次性调用两个存储库操作

此外,当您使用事务注释注释服务方法时,您不需要注释存储库方法,与事务相关的注释越多,解决问题就越复杂。

【讨论】:

    【解决方案2】:

    使用h2数据源,分布式事务成功。 但是使用mysql数据源,测试失败。 (1)首先怀疑atomikos不支持MysqlXADataSource好。 (2)第二个觉得JPA和hibernate是不是支持JTA那么好。

    然后我想用 jdbc

    @Configuration
    public class ArticleConfigure {
        @ConfigurationProperties("second.datasource")
        @Bean(name="articleDataSourceProperties")
        public DataSourceProperties secondDataSourceProperties() {
            return new DataSourceProperties();
        }
    
    
        //@Bean(name = "articleDataSource")
        @Bean(name = "articleDataSource")
        public DataSource articleDataSource() {
    
            MysqlXADataSource mdatasource = new MysqlXADataSource();
            mdatasource.setUrl(secondDataSourceProperties().getUrl());
            mdatasource.setUser(secondDataSourceProperties().getUsername());
            mdatasource.setPassword(secondDataSourceProperties().getPassword());
    
            /*JdbcDataSource h2XaDataSource = new JdbcDataSource();
            h2XaDataSource.setURL(secondDataSourceProperties().getUrl());*/
    
            //atomikos datasource configure
            com.atomikos.jdbc.AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
            xaDataSource.setXaDataSource(mdatasource);
    
            xaDataSource.setMaxPoolSize(30);
            xaDataSource.setUniqueResourceName("axds1");
    
            return xaDataSource;
        }
    
    
         @Bean(name = "twojdbcTemplate")
         public JdbcTemplate twojdbcTemplate() {
    
             return new JdbcTemplate(articleDataSource());
         }
    
    
    
    
    }
    

    事务配置。

    @Configuration
    @EnableTransactionManagement
    @ComponentScan(basePackages="cn.crazychain")
    public class TransactionConfig {
    
    
    
        @Bean(name = "userTransaction")
        public UserTransaction userTransaction() throws Throwable {
            UserTransactionImp userTransactionImp = new UserTransactionImp();
            userTransactionImp.setTransactionTimeout(10000);
            //return new BitronixTransactionManager();
            return userTransactionImp;
        }
    
    
        @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
        //@Bean(name = "atomikosTransactionManager")
        public TransactionManager atomikosTransactionManager() throws Throwable {
            UserTransactionManager userTransactionManager = new UserTransactionManager();
            userTransactionManager.setForceShutdown(false);
    
            //return TransactionManagerServices.getTransactionManager();
            return userTransactionManager;
        }
    
        @Bean(name = "customerJtaTransactionManager")
        @DependsOn({ "userTransaction", "atomikosTransactionManager" })
        public PlatformTransactionManager transactionManager() throws Throwable {
            UserTransaction userTransaction = userTransaction();
    
            TransactionManager atomikosTransactionManager = atomikosTransactionManager();
    
            return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
        }
    
    }
    

    hibernate jpa 中是否有错误。 结论是 JTA 可以很好地与 jdbc 和 AtomikosDataSourceBean 配合使用。 来源参考开源项目为https://github.com/fabiomaffioletti/mul-at.git 我的整个源代码是 https://github.com/lxiaodao/crazychain.

    【讨论】:

      【解决方案3】:

      JTA 事务管理器只有在您使用 JNDI 时才能工作。仅当数据源 bean 位于 Java/Web 容器中而不是应用程序中时,JTA tx 管理器才会侦听数据源并进行事务处理。容器。

      您需要使用 JNDI 来让 JTA 工作或开始使用 JPA 事务管理器。 JTA事务管理器主要用于分布式事务,容易出现事务回滚失败。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-17
        • 1970-01-01
        • 2016-12-27
        • 2012-04-13
        • 2011-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多