springboot jta事务回滚不起作用

时间:2017-05-30 14:18:37

标签: spring-boot spring-data jta

我想使用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 save方法)。

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;
    }
}

3 个答案:

答案 0 :(得分:0)

这是来自spring文档

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

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

Transaction propagation

尝试更改方法声明,如下所示,并给它一个

public int saveEvent1(Object eventObject1)抛出UnexpectedRollbackException

public int updateEvent2(Object eventObject2)抛出UnexpectedRollbackException

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

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

答案 1 :(得分:0)

使用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());
     }




}

TransactionConfig。

@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中是否有bug。 结论是JTA与jdbc和AtomikosDataSourceBean一起工​​作正常。 原始参考开源项目是https://github.com/fabiomaffioletti/mul-at.git 我的整个源代码是 https://github.com/lxiaodao/crazychain

答案 2 :(得分:0)

JTA事务管理器仅在您使用JNDI时才有效。只有当数据源bean在Java / Web容器中而不在应用程序中时,JTA tx管理器才会侦听数据源并引入事务。容器

您需要使用JNDI for JTA才能工作或开始使用JPA事务管理器。 JTA事务管理器主要用于分布式事务,容易发生事务回滚失败。