【发布时间】: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