Spring MVC多个数据源JTA atomikos在同一个事务中

时间:2017-02-21 17:01:14

标签: spring postgresql hibernate jta atomikos

我有一个带有Hibernate的Spring MVC项目(Spring 4.2.2 - Hibernate 4.3.6)。我想将两个数据源(两个不同的postgresql数据库)连接到它,所以我使用atomikos作为JTA的实现。我的配置全部都是注释,因此配置文件是这样的:

@Configuration
@PropertySource(value = { "classpath:hibernate.properties" })
public class HibernateConfig {

    @Autowired
    private Environment environment;

    // First DB connection
    @Primary
    @Bean(name = "sessionFactory")
    @DependsOn("setMyAtomikosSystemProps")
    public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setPackagesToScan(new String[] { "org.spring.model" });
    sessionFactory.setHibernateProperties(hibernateProperties());
    return sessionFactory;
    }

    @Bean(name = "statsSessionFactory")
    @DependsOn("setMyAtomikosSystemProps")
    public LocalSessionFactoryBean statsSessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(statsDataSource());
    sessionFactory.setPackagesToScan(new String[] { "org.spring.stats.model" });
    sessionFactory.setHibernateProperties(hibernateProperties());
    return sessionFactory;
    }

    @Primary
    @Bean(name = "dataSource")
    public DataSource dataSource() {
    AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
    ds.setUniqueResourceName("first");
    ds.setXaDataSourceClassName("org.postgresql.xa.PGXADataSource");
    ds.setXaProperties(dataSourceProperties("first"));
    ds.setMinPoolSize(5);
    ds.setMaxPoolSize(75);
    ds.setMaxIdleTime(60 * 15);
    return ds;
    }

    @Bean(name = "statsDataSource")
    public DataSource statsDataSource() {
    AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
    ds.setUniqueResourceName("second");
    ds.setXaDataSourceClassName("org.postgresql.xa.PGXADataSource");
    ds.setXaProperties(dataSourceProperties("second"));
    ds.setMinPoolSize(5);
    ds.setMaxPoolSize(75);
    ds.setMaxIdleTime(60 * 15);
    return ds;
    }

    private Properties dataSourceProperties(String database) {
    Properties p = new Properties();
    p.setProperty("user", environment.getRequiredProperty("hibernate.connection.username"));
    p.setProperty("password", environment.getRequiredProperty("hibernate.connection.password"));
    p.setProperty("serverName", environment.getRequiredProperty("hibernate.connection.url"))
    p.setProperty("portNumber", environment.getRequiredProperty("hibernate.connection.port"))
    p.setProperty("databaseName", database);
    return p;
    }

    @Bean(name = "userTransactionService")
    @DependsOn("setMyAtomikosSystemProps")
    public UserTransactionService userTransactionService() {
    UserTransactionServiceImp uts = new UserTransactionServiceImp();
    Properties prop = new Properties();
    prop.setProperty("com.atomikos.icatch.service", "com.atomikos.icatch.standalone.UserTransactionServiceFactory");
    uts.init(prop);
    return uts;
    }

    @Bean
    @DependsOn("userTransactionService")
    public UserTransactionManager AtomikosTransactionManager() {
    UserTransactionManager utm = new UserTransactionManager();
    utm.setForceShutdown(true);
    utm.setStartupTransactionService(false);
    return utm;
    }

    @Bean
    @DependsOn("userTransactionService")
    public UserTransaction AtomikosUserTransaction() {
    UserTransactionImp ut = new UserTransactionImp();
    try {
        ut.setTransactionTimeout(300);
    } catch (SystemException e) {
        e.printStackTrace();
    }
    return ut;
    }

    @Bean
    @DependsOn("userTransactionService")
    public PlatformTransactionManager JtaTransactionManager() {
    JtaTransactionManager jtaTM = new JtaTransactionManager();
    jtaTM.setTransactionManager(AtomikosTransactionManager());
    jtaTM.setUserTransaction(AtomikosUserTransaction());
    jtaTM.setAllowCustomIsolationLevels(true);
    return jtaTM;
    }

    // SharedProperties
    private Properties hibernateProperties() {
    Properties properties = new Properties();
    properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
    properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
    properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));

    // JTA
    properties.put("hibernate.current_session_context_class", "jta");
    properties.put("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
    properties.put("hibernate.transaction.jta.platform", "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");
    return properties;
    }

    @Bean
    public MethodInvokingFactoryBean setMyAtomikosSystemProps() {
    MethodInvokingFactoryBean mifb = new MethodInvokingFactoryBean();
    Properties p = new Properties();
    p.setProperty("com.atomikos.icatch.hide_init_file_path", "true");
    p.setProperty("com.atomikos.icatch.no_file", "true");
    mifb.setArguments(new Object[] { p });
    mifb.setTargetObject(java.lang.System.getProperties());
    mifb.setTargetMethod("putAll");
    return mifb;
    }
}

如果事务只涉及一个数据源,则此配置有效,但如果我创建了一个想要从两者获取数据的服务,则会收到此错误:

    GRAVE: Servlet.service() for servlet [dispatcher] in context with path [/api] threw exception [Request processing failed; nested exception is org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote] with root cause
com.atomikos.icatch.RollbackException: Prepare: NO vote
    at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.java:202)
    at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:523)
    at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:687)
    at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:282)
    at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:172)
    at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:414)
    at com.atomikos.icatch.jta.UserTransactionImp.commit(UserTransactionImp.java:86)
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1021)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy91.findById(Unknown Source)

但这不是一个超时问题,因为响应只需几秒钟。

我还从postgres收到了很长的错误列表。我" m"过滤"错误:

WARN  XAResourceTransaction - XA resource 'second': rollback for XID '3139322E3136382E322E322E746D313438373639323737373736333030303032:3139322E3136382E322E322E746D32' raised -4: the supplied XID is invalid for this XA resource
org.postgresql.xa.PGXAException: Errore durante il «rollback» di una transazione preparata 

(这个是意大利语,它说"错误期间"回滚"准备好的交易)

Caused by: org.postgresql.util.PSQLException: ERROR: prepared transactions are disabled
  Suggerimento: Set max_prepared_transactions to a nonzero value.

Caused by: org.postgresql.util.PSQLException: ERROR: prepared transactions are disabled
  Suggerimento: Set max_prepared_transactions to a nonzero value.

ERROR XAResourceTransaction - XA resource 'first': prepare for XID '3139322E3136382E322E322E746D313438373639323737373736333030303032:3139322E3136382E322E322E746D33' raised -3: the XA resource detected an internal error
org.postgresql.xa.PGXAException: Error in preparing transaction

所以它似乎需要准备好的交易,但因为只有包含它们的交易?并强制启用它们?我可以避免这个吗?

1 个答案:

答案 0 :(得分:0)

您必须编辑postgresql.conf文件,找到max_prepared_transactions并取消注释它(删除行开头的#)并设置合理的值。 请参阅:https://www.postgresql.org/docs/9.4/static/runtime-config-resource.html