2016-03-23 14 views
4

Ich benutze Frühling mit MyBatis und es arbeitete wirklich gut für eine einzelne Datenbank. Beim Versuch, eine weitere Datenbank hinzuzufügen, stieß ich auf Schwierigkeiten (siehe reproducible example on Github).Frühling mit MyBatis: erwartete einzelne zusammenpassende Bohne aber gefunden 2

Ich verwende Spring Java-Konfiguration (d. H. Nicht XML). Die meisten Beispiele, die ich gesehen habe, zeigen, wie dies mit XML erreicht wird.

Ich habe zwei Datenkonfigurationsklassen (A & B) wie folgt aus:

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

Zwei Mapper, und einen Service, der die Mapper autowires:

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

} 

Die Anwendung wird nicht gestartet:

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 

Ich habe gelesen, dass es möglich ist, diezu verwenden 0 Annotation, um das Autowiring zu entschlüsseln, obwohl ich nicht sicher war, wo ich es hinzufügen könnte.

Können Sie sehen, wo ich falsch liege?

+0

Können Sie die vollständige Fehlermeldung posten? Normalerweise sagt spring Ihnen das autowired Feld und die Bean, die den Fehler verursachen. – ben75

+0

'@Qaulifier (" name_of_bean ")' kann vor oder nach der '@ Autowired' Annotation des bestimmten Felds platziert werden, auf das ich zielen möchte – BretC

+0

Danke @ Ben75. Ich postete die volle Ausgabe zu: https://gist.github.com/alexwoolford/1f3e799deb3be32a4356 –

Antwort

1

Am Ende haben wir jeden Mapper in einem eigenen Ordner:

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

Wir haben dann zwei DataConfig Klassen, eine für jeden Datenbank. Die @MapperScan Annotation löste das Problem expected single matching bean but found 2.

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

Es war notwendig, die @Primary Anmerkung zu den Bohnen in einer der DataConfig Klassen hinzuzufügen:

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

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

Vielen Dank an alle, die geholfen haben. Zweifellos gibt es mehr als einen Weg, dies zu tun. Ich habe versucht @Qualifier und @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) wie von @eduardlofitskyi und @GeminiKeith empfohlen, aber das erzeugte einige weitere Fehler.

Falls es sinnvoll ist, die Lösung, die für uns gearbeitet wird hier gepostet: https://github.com/alexwoolford/mybatis-spring-multiple-mysql-reproducible-example

2

Wenn Sie zwei Datenquellen gleichzeitig verwenden möchten und diese nicht primär und sekundär sind, sollten Sie DataSourceAutoConfiguration durch @EnableAutoConfiguration(excludes = {DataSourceAutoConfiguration.class}) in Ihrer Anwendung deaktivieren, die mit @SpringBootApplication gekennzeichnet ist. Danach können Sie Ihre eigene SqlSessionFactory erstellen und Ihre eigene DataSource bündeln. Wenn Sie auch DataSourceTransactionManager verwenden möchten, sollten Sie das auch tun.

In diesem Fall haben Sie nicht DataSourceAutoConfiguration deaktiviert, so wird Feder-Framework versuchen, @Autowired nur eine , aber zwei, Fehler auftritt.

Wie bereits erwähnt, sollten Sie DataSourceAutoConfiguration deaktivieren und manuell konfigurieren.

Sie können Datenquelle Auto-Konfiguration deaktivieren, wie folgend:

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

Und wenn Sie sind wirklich mehrere Datenbanken gleichzeitig nutzen, empfehle ich Ihnen manuell richtige Bohne zu registrieren, wie zum Beispiel:

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"); 
    } 
} 
+0

Was für eine hilfreiche Antwort !!! Entweder '@EnableAutoConfiguration (excludes = DataSourceAutoConfiguration.class) 'oder' @SpringBootApplication (exclude = DataSourceAutoConfiguration.class) '. – sjngm

+0

Danke! verbrachte eine Stunde dafür! –

0

können Sie verwenden @Qualifier Anmerkung

Das Problem ist, dass Sie zwei der gleichen Art Bohnen im Frühjahr Behälter haben. Und wenn Sie Autowire-Beans versuchen, kann Spring nicht auflösen, welche Bean in Feld injizieren.

Die @Qualifier Annotation ist die wichtigste Möglichkeit, mit Qualifikationsmerkmalen zu arbeiten. Sie kann neben @Autowired oder @Inject am Injektionspunkt angewendet werden, um anzugeben, welche Bohne injiziert werden soll.

Also, Ihr DBService sollte wie folgt aussehen:

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

} 
0

Ich hatte das gleiche Problem und konnte nicht meine Frühling Boot-Anwendung starten, und durch die beanstandete Klasse umbenennen und alle Schichten, die ausgeteilt damit begann seltsamerweise die Anwendung erfolgreich.

Ich habe die Klassen UOMService, UOMServiceImplUOMRepository und UOMRepositoryImpl. Ich habe sie in UomService, UomServiceImpl, UomRepository und UomRepositoryImpl umbenannt, und das hat das Problem gelöst!