You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决JtaTransactionManager需指定transactionManager属性的错误

多数据源JTA事务处理异常排查与解决

异常信息

Exception in thread "main" org.springframework.transaction.TransactionSuspensionNotSupportedException: JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: specify the 'transactionManager' or 'transactionManagerName' property
    at org.springframework.transaction.jta.JtaTransactionManager.doJtaSuspend(JtaTransactionManager.java:941)
    at org.springframework.transaction.jta.JtaTransactionManager.doSuspend(JtaTransactionManager.java:923)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.suspend(AbstractPlatformTransactionManager.java:575)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:420)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:349)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:420)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:257)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    at xyz.hollysys.spring.mybatis_super.service.ApplicationService1$$EnhancerBySpringCGLIB$$f8684ee3.saveData(<generated>)
    at xyz.hollysys.spring.mybatis_super.service.UserService.save(UserService.java:28)
    at xyz.hollysys.spring.mybatis_super.service.UserService$$FastClassBySpringCGLIB$$4da2330f.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:708)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    at xyz.hollysys.spring.mybatis_super.service.UserService$$EnhancerBySpringCGLIB$$7b31d501.save(<generated>)
    at xyz.hollysys.spring.mybatis_super.App.main(App.java:21)

问题场景

使用JdbcTemplate执行批量更新操作,跨两个不同数据源处理分布式事务时触发上述异常。

相关代码

CustomerRepository.java

@Transactional(propagation = Propagation.REQUIRED)
public int[] saveSuccessBatch(List<Customer> customerList) {
    return jdbcTemplate.batchUpdate("insert into customer (age,first_name, last_name) values(?,?,?)",
            new BatchPreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    Customer customer = customerList.get(i);
                    ps.setInt(1, customer.getAge());
                    ps.setString(2, customer.getFirstName());
                    ps.setString(3, customer.getLastName());
                }

                @Override
                public int getBatchSize() {
                    return customerList.size();
                }
            });
}

AddressRepository.java

@Transactional(propagation = Propagation.REQUIRED)
public int[] saveSuccessBatch(List<Address> addressList) {
    return jdbcTemplate.batchUpdate("insert into address (city,street, zipCode) values(?,?,?)",
            new BatchPreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    Address address = addressList.get(i);
                    ps.setString(1, address.getCity());
                    ps.setString(2, address.getStreet());
                    ps.setInt(3, address.getZipCode());
                }

                @Override
                public int getBatchSize() {
                    return addressList.size();
                }
            });
}

Service.java

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveData(List<List<Customer>> mainCustomerList, List<List<Address>> mainAddressList, int index) {
    customerRepository.saveSuccessBatch(mainCustomerList.get(index));
    addressRepository.saveSuccessBatch(mainAddressList.get(index));
}

JdbcConfiguration.java

package xyz.hollysys.spring.mybatis_super.config;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.transaction.SystemException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;

import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.cj.jdbc.MysqlXADataSource;

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages ={  "xyz.hollysys.spring.mybatis_super.dao","xyz.hollysys.spring.mybatis_super.service"})
@PropertySource(value = { "classpath:jdbc.properties" })
public class JdbcConfiguration {
    
    // master database
    @Value("${jdbc.driverClassName:com.mysql.jdbc.Driver}")
    private String driverClassName;

    @Value("${jdbc.url:jdbc:mysql://localhost:3306/db1}")
    private String url;

    @Value("${jdbc.username:root}")
    private String username;

    @Value("${jdbc.password:}")
    private String password;
    
    // slave database
    @Value("${jdbc2.driverClassName:com.mysql.jdbc.Driver}")
    private String driverClassName2;

    @Value("${jdbc2.url:jdbc:mysql://localhost:3306/db2}")
    private String url2;

    @Value("${jdbc2.username:root}")
    private String username2;

    @Value("${jdbc2.password:}")
    private String password2;

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
       return new PropertySourcesPlaceholderConfigurer();
    }
    
    @Bean(name="master",initMethod = "init", destroyMethod = "close")
    public AtomikosDataSourceBean AtomikosDataSourceA(){
        AtomikosDataSourceBean bean =  new AtomikosDataSourceBean();
        bean.setXaDataSourceClassName(MysqlXADataSource.class.getCanonicalName());
        bean.setXaProperties(xaPropertiesA());
        bean.setUniqueResourceName("master");
        bean.setMinPoolSize(1);
        bean.setMaxPoolSize(3);
        bean.setMaxIdleTime(60);
        bean.setPoolSize(1);
        return bean;
        
    }
    
    @Bean(name="slave",initMethod = "init", destroyMethod = "close")
    public AtomikosDataSourceBean AtomikosDataSourceB(){
        AtomikosDataSourceBean bean =  new AtomikosDataSourceBean();
        bean.setXaDataSourceClassName(MysqlXADataSource.class.getCanonicalName());
        bean.setXaProperties(xaPropertiesB());
        bean.setUniqueResourceName("slave");
        bean.setMinPoolSize(1);
        bean.setMaxPoolSize(3);
        bean.setMaxIdleTime(60);
        bean.setPoolSize(1);
        return bean;
        
    }
    
    @Bean
    public Properties xaPropertiesA() {
        Properties props = new Properties();
        props.setProperty("URL", url);
        props.setProperty("user", username);
        props.setProperty("password", password);
        return props;
    }   
    
    @Bean
    public Properties xaPropertiesB() {
        Properties props = new Properties();
        props.setProperty("URL", url2);
        props.setProperty("user", username2);
        props.setProperty("password", password2);
        return props;
    }

    @Bean
    public Map<Object, Object> targetDataSourcesJta() {
        Map<Object, Object> map = new HashMap<Object, Object>();
        map.put("master", AtomikosDataSourceA());
        map.put("slave", AtomikosDataSourceB());
        
        return map;
    }
    
    @Bean(name="jdbcTemplateA")
    public JdbcTemplate jdbcTempalteA(){
        JdbcTemplate template = new JdbcTemplate();
        template.setDataSource(AtomikosDataSourceA());
        return template;
    }
    
    @Bean(name="jdbcTemplateB")
    public JdbcTemplate jdbcTempalteB(){
        JdbcTemplate template = new JdbcTemplate();
        template.setDataSource(AtomikosDataSourceB());
        return template;
    }
    
    @Bean
    public UserTransactionManager userTransactionManager(){
        UserTransactionManager userJta = new UserTransactionManager();
        userJta.setForceShutdown(true);
        
        return userJta;
    }
    
    @Bean
    public UserTransactionImp userTransactionImp(){
        UserTransactionImp userJtaImpl = new UserTransactionImp();
        try {
            userJtaImpl.setTransactionTimeout(300);
        } catch (SystemException e) {
            e.printStackTrace();
        }
        return userJtaImpl;
    }
    
    @Bean(name="transactionManager")
    public JtaTransactionManager jtaTransactionManager(){
        JtaTransactionManager jta = new JtaTransactionManager();
        //jta.setTransactionManager(userTransactionManager());
        jta.setUserTransaction(userTransactionImp());
        return jta;
    }
    
}

问题原因

异常提示明确:JtaTransactionManager需要配置JTA TransactionManager才能支持事务挂起。当前配置中仅注入了UserTransaction,但注释掉了TransactionManager的设置,导致处理REQUIRES_NEW传播特性的事务时,无法挂起现有事务上下文,触发异常。

解决方案

1. 修复JtaTransactionManager配置

取消jta.setTransactionManager(userTransactionManager())的注释,同时注入UserTransaction和TransactionManager实例:

@Bean(name="transactionManager")
public JtaTransactionManager jtaTransactionManager(){
    JtaTransactionManager jta = new JtaTransactionManager();
    jta.setTransactionManager(userTransactionManager()); // 启用该行配置
    jta.setUserTransaction(userTransactionImp());
    return jta;
}

2. 优化事务注解(可选)

Repository层的@Transactional(propagation = Propagation.REQUIRED)可以移除,因为Repository方法已经处于Service层的事务上下文之中,REQUIRED会自动加入现有事务,无需重复标注。

3. 验证XA数据源兼容性

确保使用的MySQL驱动版本支持XA事务(mysql-connector-java 5.1.3+ 或 mysql-connector-j 8.0+),避免因驱动不支持导致分布式事务失效。

内容的提问来源于stack exchange,提问作者Dharmin Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 03:22:03