rakeeee
rakeeee

Reputation: 1073

Spring boot Mybatis multiple datasource

I am using Spring boot 2.0.3 and mybatis with PostgreSql.

I am trying to set up multiple data source connection as follows by following https://programmer.help/blogs/spring-boot-integrates-mybatis-multiple-data-sources.html.

Datasource1

@Configuration
@MapperScan(basePackages = "com.repositories.StaRepository", sqlSessionFactoryRef = "sqlPromptSessionFactory", annotationClass = Mapper.class)
//SqlSessionFactory is created from DB1 and then a SqlSessionTemplate is created from the created SqlSessionFactory.
public class MyBatisConfigPrompt {

    @Bean(name = "DB1")
    @ConfigurationProperties(prefix = "spring.datasource.pro")
    public DruidDataSource DB1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "sqlProSessionFactory")
    SqlSessionFactory sqlProSessionFactory() {
        SqlSessionFactory sessionFactory = null;
        try {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(DB1());
            sessionFactory = bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sessionFactory;
    }

    @Bean
    public MapperScannerConfigurer proMapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.repositories.StaRepository");
        configurer.setSqlSessionFactoryBeanName("sqlProSessionFactory");
        return configurer;
    }

}

Datasource2

@Configuration
@MapperScan(basePackages = "com.repositories.ContDBRepository", sqlSessionFactoryRef = "sqlContSessionFactory", annotationClass = Mapper.class)
//SqlSessionFactory is created from contDB and then a SqlSessionTemplate is created from the created SqlSessionFactory.
public class MyBatisConfigCont {
    @Bean(name = "contDB")
    @ConfigurationProperties(prefix = "spring.datasource.cont")
    public DruidDataSource contDB() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "sqlContSessionFactory")
    SqlSessionFactory sqlContSessionFactory() {
        SqlSessionFactory sessionFactory = null;
        try {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(contDB());
            sessionFactory = bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sessionFactory;
    }

    @Bean
    public MapperScannerConfigurer contMapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.repositories.ContDBRepository");
        configurer.setSqlSessionFactoryBeanName("sqlContSessionFactory");
        return configurer;
    }
}

I have also a ContDBRepository.class with @Mapper Annotation and ContDBRepository.xml and same as StaRepository.class with @Mapper Annotation and StaRepository.xml in same package.

With the above configuration i am getting ERROR

No qualifying bean of type 'org.apache.ibatis.session.SqlSessionFactory' available: expected single matching bean but found 2: sqlContSessionFactory,sqlProSessionFactory

As a fix to the above error i set @Primary to one of the SqlSessionFactory but other SqlSessionFactory is never called when i want to use second datasource.

Can anyone help what i am missing.

Upvotes: 1

Views: 12716

Answers (1)

Chengcheng Xu
Chengcheng Xu

Reputation: 21

UPDATE 20210301

This example help me to find a solution for makeing sure I could use the specific datasource.

The basic idea is to create a abstract data source as the router giving to the mybatis config. Then use a enum and @interface as the selector and adding them before any interface you want a specific data source. Finally AOP is the program paradigm to define how to change the data source.

Some key points:

  1. AbstractRoutingDataSource will be the key to store our whole datasources.
  2. @interface will be the key to create our router for our different ServiceImpl with specific interface, which will not need @Repository anymore, by adding that to any interface you override with the specific data source type.
  3. @Aspect and @Pointcut will be the key to guarantee our router will work properly.

ORIGINAL

I found the same question for most example online about multiple data source.

The example you saw is not checked very much because he only use two localhost with same database info and table info.

The config example used:

spring.datasource.one.url=jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8
spring.datasource.one.username=root
spring.datasource.one.password=123456
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.two.url=jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
spring.datasource.two.username=root
spring.datasource.two.password=123456
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource

All the example I tried, all failed at get table from wrong database, which actually means the @Repository only can get one DataSource or config.

Upvotes: 2

Related Questions