Spring 与 MyBatis:预期单个匹配 bean 但找到 2
Spring with MyBatis: expected single matching bean but found 2
我一直在将 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
注释来消除自动装配的歧义,尽管我不确定在哪里添加它。
你能看出我错在哪里吗?
如果您想同时使用两个数据源并且它们不是主要的和次要的,您应该在由 @SpringBootApplication
注释的应用程序上通过 @EnableAutoConfiguration(excludes = {DataSourceAutoConfiguration.class})
禁用 DataSourceAutoConfiguration
。之后,您可以创建自己的 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");
}
}
您可以使用@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();
}
}
最后,我们将每个映射器放在自己的文件夹中:
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 {
有必要在 DataConfig
类:
之一的 bean 中添加 @Primary
注释
@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
我遇到了同样的问题,无法启动我的 Spring 引导应用程序,并且通过重命名有问题的 class 和处理它的所有层,奇怪的是应用程序成功启动。
我有 classes UOMService
、UOMServiceImpl
UOMRepository
和 UOMRepositoryImpl
。我将它们重命名为 UomService
、UomServiceImpl
、UomRepository
和 UomRepositoryImpl
,这解决了问题!
我一直在将 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
注释来消除自动装配的歧义,尽管我不确定在哪里添加它。
你能看出我错在哪里吗?
如果您想同时使用两个数据源并且它们不是主要的和次要的,您应该在由 @SpringBootApplication
注释的应用程序上通过 @EnableAutoConfiguration(excludes = {DataSourceAutoConfiguration.class})
禁用 DataSourceAutoConfiguration
。之后,您可以创建自己的 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");
}
}
您可以使用@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();
}
}
最后,我们将每个映射器放在自己的文件夹中:
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 {
有必要在 DataConfig
类:
@Primary
注释
@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
我遇到了同样的问题,无法启动我的 Spring 引导应用程序,并且通过重命名有问题的 class 和处理它的所有层,奇怪的是应用程序成功启动。
我有 classes UOMService
、UOMServiceImpl
UOMRepository
和 UOMRepositoryImpl
。我将它们重命名为 UomService
、UomServiceImpl
、UomRepository
和 UomRepositoryImpl
,这解决了问题!