Search code examples
javaspringspring-bootmybatisspring-mybatis

Spring with MyBatis: expected single matching bean but found 2


I've been using Spring with MyBatis and it's been working really well for a single database. I ran into difficulties when trying to add another database (see reproducible example on Github).

I'm using Spring Java configuration (i.e. not XML). Most of the examples I've seen show how to achieve this using XML.

I have two data configuration classes (A & B) like this:

@Configuration
@MapperScan("io.woolford.database.mapper")
public class DataConfigDatabaseA {

    @Bean(name="dataSourceA")
    public DataSource dataSourceA() throws SQLException {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        dataSource.setDriver(new com.mysql.jdbc.Driver());
        dataSource.setUrl("jdbc:mysql://" + dbHostA + "/" + dbDatabaseA);
        dataSource.setUsername(dbUserA);
        dataSource.setPassword(dbPasswordA);
        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSourceA());
        return sessionFactory.getObject();
    }
}

Two mappers, and a service that autowires the mappers:

@Service
public class DbService {

    @Autowired
    private DbMapperA dbMapperA;

    @Autowired
    private DbMapperB dbMapperB;

    public List<Record> getDabaseARecords(){
        return dbMapperA.getDatabaseARecords();
    }

    public List<Record> getDabaseBRecords(){
        return dbMapperB.getDatabaseBRecords();
    }

}

The application won't start:

Error creating bean with name 'dataSourceInitializer': 
  Invocation of init method failed; nested exception is 
    org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
      No qualifying bean of type [javax.sql.DataSource] is defined: 
        expected single matching bean but found 2: dataSourceB,dataSourceA

I've read that it's possible to use the @Qualifier annotation to disambiguate the autowiring, though I wasn't sure where to add it.

Can you see where I'm going wrong?


Solution

  • In the end, we put each mapper in its own folder:

    src/main/java/io/woolford/database/mapper/a/DbMapperA.java
    src/main/java/io/woolford/database/mapper/c/DbMapperB.java
    

    We then created two DataConfig classes, one for each database. The @MapperScan annotation resolved the expected single matching bean but found 2 issue.

    @Configuration
    @MapperScan(value = {"io.woolford.database.mapper.a"}, sqlSessionFactoryRef="sqlSessionFactoryA")
    public class DataConfigDatabaseA {
    

    It was necessary to add the @Primary annotation to the beans in one of the DataConfig classes:

    @Bean(name="dataSourceA")
    @Primary
    public DataSource dataSourceA() throws SQLException {
        ...
    }
    
    @Bean(name="sqlSessionFactoryA")
    @Primary
    public SqlSessionFactory sqlSessionFactoryA() throws Exception {
        ...
    }
    

    Thanks to everyone who helped. No doubt, there's more than one way to do this. I did try @Qualifier and @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) as recommended by @eduardlofitskyi and @GeminiKeith, but that generated some further errors.

    In case it's useful, the solution that worked for us is posted here: https://github.com/alexwoolford/mybatis-spring-multiple-mysql-reproducible-example