【问题标题】:Spring with MyBatis: expected single matching bean but found 2Spring with MyBatis:预期单个匹配 bean 但找到 2
【发布时间】:2016-07-11 02:53:12
【问题描述】:

我一直在将 Spring 与 MyBatis 一起使用,它在单个数据库中运行得非常好。我在尝试添加另一个数据库时遇到了困难(请参阅reproducible example on Github)。

我正在使用 Spring Java 配置(即不是 XML)。我见过的大多数示例都展示了如何使用 XML 来实现这一点。

我有两个这样的数据配置类(A 和 B):

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

两个映射器和一个自动装配映射器的服务:

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

}

应用程序无法启动:

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

我读到可以使用 @Qualifier 注释来消除自动装配的歧义,但我不确定在哪里添加它。

你能看出我哪里错了吗?

【问题讨论】:

标签: java spring spring-boot mybatis spring-mybatis


【解决方案1】:

我遇到了同样的问题,无法启动我的 Spring Boot 应用程序,通过重命名有问题的类和处理它的所有层,奇怪的是应用程序成功启动了。

我有 UOMServiceUOMServiceImplUOMRepositoryUOMRepositoryImpl 的课程。我将它们重命名为UomServiceUomServiceImplUomRepositoryUomRepositoryImpl,这样就解决了问题!

【讨论】:

    【解决方案2】:

    最后,我们将每个映射器放在自己的文件夹中:

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

    然后我们创建了两个DataConfig 类,每个数据库一个。 @MapperScan 注解解决了expected single matching bean but found 2 问题。

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

    必须将@Primary 注释添加到DataConfig 类之一中的bean:

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

    感谢所有提供帮助的人。毫无疑问,有不止一种方法可以做到这一点。我确实按照@eduardlofitskyi 和@GeminiKeith 的建议尝试了@Qualifier@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}),但这又产生了一些错误。

    如果它有用,对我们有用的解决方案发布在这里:https://github.com/alexwoolford/mybatis-spring-multiple-mysql-reproducible-example

    【讨论】:

      【解决方案3】:

      可以使用@Qualifier注解

      问题是你在 Spring 容器中有两个相同类型的 bean。当你尝试自动装配 bean 时,Spring 无法解析哪个 bean 注入到字段

      @Qualifier 注释是使用限定符的主要方式。它可以在注入点与@Autowired@Inject 一起应用,以指定要注入的bean。

      所以,您的 DbService 应该如下所示:

          @Service
          public class DbService {
      
          @Autowired
          @Qualifier("dataSourceA")
          private DbMapperA dbMapperA;
      
          @Autowired
          @Qualifier("dataSourceB")
          private DbMapperB dbMapperB;
      
          public List<Record> getDabaseARecords(){
              return dbMapperA.getDatabaseARecords();
          }
      
          public List<Record> getDabaseBRecords(){
              return dbMapperB.getDatabaseBRecords();
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        如果您想同时使用两个数据源并且它们不是主要和次要的,您应该在您的应用程序上禁用DataSourceAutoConfiguration @EnableAutoConfiguration(excludes = {DataSourceAutoConfiguration.class}) @SpringBootApplication 注释。之后,您可以创建自己的SqlSessionFactory 并捆绑您自己的DataSource。如果你也想使用DataSourceTransactionManager,你也应该这样做。

        在这种情况下,你还没有禁用DataSourceAutoConfiguration,所以spring框架会尝试@Autowired只有一个DataSource但是有两个,错误发生。

        正如我之前所说,您应该禁用 DataSourceAutoConfiguration 并手动配置它。

        您可以按如下方式禁用数据源自动配置:

        @SpringBootApplication
        @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
        public class YourApplication implements CommandLineRunner {
            public static void main (String... args) {
                SpringApplication.run(YourApplication.class, args);
            }
        }
        

        如果你真的想同时使用多个数据库,我建议你手动注册适当的bean,例如:

        package xyz.cloorc.boot.mybatis;
        
        import org.apache.commons.dbcp.BasicDataSource;
        import org.apache.ibatis.session.SqlSessionFactory;
        import org.mybatis.spring.SqlSessionFactoryBean;
        import org.mybatis.spring.support.SqlSessionDaoSupport;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.stereotype.Repository;
        
        import javax.annotation.PostConstruct;
        import javax.annotation.Resource;
        import javax.sql.DataSource;
        
        @Configuration
        public class SimpleTest {
        
            private DataSource dsA;
            private DataSource dsB;
        
            @Bean(name = "dataSourceA")
            public DataSource getDataSourceA() {
                return dsA != null ? dsA : (dsA = new BasicDataSource());
            }
        
            @Bean(name = "dataSourceB")
            public DataSource getDataSourceB() {
                return dsB != null ? dsB : (dsB = new BasicDataSource());
            }
        
            @Bean(name = "sqlSessionFactoryA")
            public SqlSessionFactory getSqlSessionFactoryA() throws Exception {
                // set DataSource to dsA
                return new SqlSessionFactoryBean().getObject();
            }
        
            @Bean(name = "sqlSessionFactoryB")
            public SqlSessionFactory getSqlSessionFactoryB() throws Exception {
                // set DataSource to dsB
                return new SqlSessionFactoryBean().getObject();
            }
        }
        
        @Repository
        public class SimpleDao extends SqlSessionDaoSupport {
        
            @Resource(name = "sqlSessionFactoryA")
            SqlSessionFactory factory;
        
            @PostConstruct
            public void init() {
                setSqlSessionFactory(factory);
            }
        
            @Override
            public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
                super.setSqlSessionFactory(sqlSessionFactory);
            }
        
            public <T> T get (Object id) {
                return super.getSqlSession().selectOne("sql statement", "sql parameters");
            }
        }
        

        【讨论】:

        • 多么有帮助的答案啊!!! @EnableAutoConfiguration(excludes = DataSourceAutoConfiguration.class)@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
        • 谢谢!为此花了一个小时!
        猜你喜欢
        • 1970-01-01
        • 2012-01-14
        • 2016-04-12
        • 2018-04-22
        • 1970-01-01
        • 1970-01-01
        • 2013-04-20
        • 2014-08-18
        • 1970-01-01
        相关资源
        最近更新 更多