java Spring with MyBatis:预期单个匹配 bean,但发现 2

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36184546/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 01:02:12  来源:igfitidea点击:

Spring with MyBatis: expected single matching bean but found 2

javaspringspring-bootmybatisspring-mybatis

提问by Alex Woolford

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).

我一直在将 Spring 与 MyBatis 一起使用,它在单个数据库上运行得非常好。我在尝试添加另一个数据库时遇到了困难(请参阅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.

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

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

我有两个这样的数据配置类(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();
    }
}

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 @Qualifierannotation to disambiguate the autowiring, though I wasn't sure where to add it.

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

Can you see where I'm going wrong?

你能看出我哪里错了吗?

采纳答案by Alex Woolford

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 DataConfigclasses, one for each database. The @MapperScanannotation resolved the expected single matching bean but found 2issue.

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

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

It was necessary to add the @Primaryannotation to the beans in one of the DataConfigclasses:

有必要@Primary在其中一个DataConfig类中向 bean添加注释:

@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 @Qualifierand @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})as recommended by @eduardlofitskyi and @GeminiKeith, but that generated some further errors.

感谢所有帮助过的人。毫无疑问,有不止一种方法可以做到这一点。我曾尝试@Qualifier@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})所推荐的@eduardlofitskyi和@GeminiKeith,但产生了一些进一步的错误。

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

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

回答by Gemini Keith

If you want to use two data sources at same time and they are not primary and secondary, you should disable DataSourceAutoConfigurationby @EnableAutoConfiguration(excludes = {DataSourceAutoConfiguration.class})on your application annotated by @SpringBootApplication. Afterwards, you can create your own SqlSessionFactoryand bundle your own DataSource. If you also want to use DataSourceTransactionManager, you should do that too.

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

In this case, you haven't disabled DataSourceAutoConfiguration, so spring framework will try to @Autowiredonly one DataSourcebut got two, error occurs.

在这种情况下,您还没有禁用DataSourceAutoConfiguration,因此 spring 框架将尝试@Autowired只执行一个DataSource但得到两个,就会发生错误。

As what I've said before, you should disable DataSourceAutoConfigurationand configure it manually.

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

You can disable data source auto configuration as following:

您可以禁用数据源自动配置,如下所示:

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

And if you are really want to use multiple databases at same time, I suggest you to registering proper bean manually, such as:

如果你真的想同时使用多个数据库,我建议你手动注册合适的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");
    }
}

回答by Ahmed Tawila

I had the same issue and could not start my Spring Boot application, and by renaming the offending class and all the layers that dealt with it, strangely the application started successfully.

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

I have the classes UOMService, UOMServiceImplUOMRepositoryand UOMRepositoryImpl. I renamed them to be UomService, UomServiceImpl, UomRepositoryand UomRepositoryImpland that solved the problem!

我有课UOMServiceUOMServiceImplUOMRepositoryUOMRepositoryImpl。我给他们改名为UomServiceUomServiceImplUomRepositoryUomRepositoryImpl而解决了这个问题!

回答by eduardlofitskyi

You can use @Qualifierannotation

您可以使用@Qualifier注释

The problem is that you have two the same type beans in Spring container. And when you try autowire beans, Spring cannot resolve which bean inject to field

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

The @Qualifierannotation is the main way to work with qualifiers. It can be applied alongside @Autowiredor @Injectat the point of injection to specify which bean you want to be injected.

@Qualifier注释是预选赛工作的主要途径。它可以与注入一起@Autowired@Inject在注入点应用,以指定要注入的 bean。

So, your DbService should look like this:

因此,您的 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();
    }

}