2017-02-21 4 views
0

J'ai un projet Spring MVC avec Hibernate (Spring 4.2.2 - Hibernate 4.3.6). Je voulais connecter deux sources de données (deux bases de données postgresql différentes), donc j'ai utilisé atomikos comme implémentation pour JTA. J'ai ma configuration tout par des annotations, de sorte que le fichier de configuration est la suivante:Spring MVC sources de données multiples JTA atomikos dans la même transaction

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

Cette configuration fonctionne si la transaction ne comporte qu'une seule source de données, mais si je crée un service qui veut obtenir des données à la fois, je reçois cette erreur :

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) 

Mais ce n'est pas un problème de délai d'attente car la réponse arrive dans quelques secondes.

Je reçois aussi une très longue liste d'erreurs de postgres. Je suis « filtrage » les erreurs:

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 

(celui-ci est en italien, il est dit « erreur lors de la « rollback » d'une transaction préparée)

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 

Il semble donc qu'il a besoin préparé transaction, mais parce que c'est seulement pour la transaction qui inclut les deux? Et il est obligatoire de les activer? Puis-je éviter cela?

Répondre

0

Vous devez éditer votre fichier postgresql.conf, trouver max_prepared_transactions et le décommenter (enlever le # à la début de la ligne) et définissez une valeur raisonnable Voir: https://www.postgresql.org/docs/9.4/static/runtime-config-resource.html

+0

Je sais comment activer la transaction préparée, mais je les ai nécessairement activés? Et pourquoi ça marche sans eux? – zuno