Configuring multiple datasources using SpringBoot and Atomikos

Since I didn't find any article explaining how to use the auto configuration to configure Atomikos on SpringBoot, I decided to write this post. I hope it can be useful to someone. ;)

Dependencies

Assuming you are developing a web application and want to use Atomikos to provide distributed transactions over multiple datasources, you will need only two dependencies:

def springBootVersion = 'YOUR_SPRING_BOOT_VERSION_HERE'

compile group: 'org.springframework.boot', name: 'spring-boot-starter-jta-atomikos', version: springBootVersion
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion

The application.yml

If you are using SpringBoot, you need to have an application.properties or an application.yml. I find YAML much easier to read than a properties file, so, that's why I'm using it in here.

spring:
    profiles:
      active: prod

    jta:
      enabled: true
      service: com.atomikos.icatch.standalone.UserTransactionServiceFactory
      max-actives: 200
      enable-logging: false

      atomikos: 
        datasource:
          one:
            unique-resource-name: dataSourceOne
            max-pool-size: 5
            min-pool-size: 1
            max-life-time: 20000
            borrow-connection-timeout: 10000
            xa-data-source-class-name: org.h2.jdbcx.JdbcDataSource
            xa-properties:
              user: sa
              password:
              URL: jdbc:h2:mem:one;DB_CLOSE_DELAY=-1

          two:
            unique-resource-name: dataSourceTwo
            max-pool-size: 5
            min-pool-size: 1
            max-life-time: 20000
            borrow-connection-timeout: 10000
            xa-data-source-class-name: org.h2.jdbcx.JdbcDataSource
            xa-properties:
              user: sa
              password:
              URL: jdbc:h2:mem:two;DB_CLOSE_DELAY=-1

          three:
            unique-resource-name: dataSourceThree
            max-pool-size: 5
            min-pool-size: 1
            max-life-time: 20000
            borrow-connection-timeout: 10000
            xa-data-source-class-name: org.h2.jdbcx.JdbcDataSource
            xa-properties:
              user: sa
              password:
              URL: jdbc:h2:mem:three;DB_CLOSE_DELAY=-1

The Configuration class

After you have configured the application.yml, you need to create three beans: the datasources. We need to create each of them based on each datasource that you configured in the application.yml properties.

@Configuration
@EnableConfigurationProperties
@EnableAutoConfiguration
public class SpringContext {
    @Bean
    @ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.one")
    public DataSource dataSourceOne() {
        return new AtomikosDataSourceBean();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.two")
    public DataSource dataSourceTwo() {
        return new AtomikosDataSourceBean();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.three")
    public DataSource dataSourceThree() {
        return new AtomikosDataSourceBean();
    }
}

That could be all, but, unfortunately, when you use the @EnableAutoConfiguration annotation, all auto configurations are enabled, and so are the configurations that awaits only one datasource. So, if you run this, probably you will get this exception:

(...) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 3: dataSourceOne,dataSourceTwo,dataSourceThree. (...)

The Spring doc says you need to set a @Primary datasource for resolving this issue, but, if you don't have a primary datasource, there is no sense in doing it at all.

So, if you want to use multiple datasources and you still don't want to set a primary datasource, you need to disable every auto configuration that uses a default datasource. So, I came up with this to resolve the problem:

@Configuration
@EnableConfigurationProperties
@EnableAutoConfiguration(exclude = {
        DataSourceAutoConfiguration.class,
        HibernateJpaAutoConfiguration.class, //if you are using Hibernate
        DataSourceTransactionManagerAutoConfiguration.class
})
public class SpringContext {
    (...)
}

So, after that you can run your SpringBoot application using JTA provided by Atomikos. ;)

Any question? Please, leave a message.

[]'S

Gabriel Francisco

Software Engineer at UOL, 24 years, under graduated in Computer Science and graduated in Service-oriented Software Engineering. Like playing guitar once in a while. I'm pretty sure I'm really boring.

São Paulo

comments powered by Disqus