Avatar
1
toilahtc Beginner
toilahtc Beginner
Làm sao để cấu hình tới 2 database trong 1 project spring boot?
Làm sao để cấu hình tới 2 database trong 1 project spring boot?

Em gặp lỗi này: No qualifying bean of type 'org.springframework.transaction.PlatformTransactionManager' available: expected single matching bean but found 2 khi sử dụng @Transactional. Em đã có đánh dấu @Transactional(transactionManager = "mmTransactionManager")

Anh em nào biết thì mong giúp đỡ em ạ, em cảm ơn. Do text quá dài nên em xin gửi cấu hình ở dưới comment.

  • Answer
spring datasource
Remain: 5
6 Answers
Avatar
toilahtc Beginner
toilahtc Beginner
Em cấu hình trong spring.xml như sau:
 <!– JPA –>
 
 <jpa:repositories base-package="pakageOfDAO1"
         entity-manager-factory-ref="mmEntityManager"
         transaction-manager-ref="mmTransactionManager">
 
 </jpa:repositories>
 <jpa:repositories base-package="pakageOFDAO"
         entity-manager-factory-ref="mmEtcEntityManager"
         transaction-manager-ref="mmEtcTransactionManager"
 >
 
 </jpa:repositories>
 
 <!– Datasource ETC. –>
 <bean id="secondDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="${spring.secondDatasource.driverClassName}"/>
     <property name="url" value="${spring.secondDatasource.url}"/>
     <property name="username" value="${spring.secondDatasource.username}"/>
     <property name="password" value="${spring.secondDatasource.password}"/>
 </bean>
 
 <!– Datasource, that is currently hsqldb (in-memory database). –>
 <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="${spring.datasource.driver-class-name}"/>
     <property name="url" value="${spring.datasource.url}"/>
         <property name="username" value="${spring.datasource.username}"/>
     <property name="password" value="${spring.datasource.password}"/>
 </bean>
 
 <bean id="hibernateJpaVendorAdapter"
       class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
 <!– EntityEtcManagerFactory –>zxczx
 <bean id="mmEtcEntityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
       p:packagesToScan="pakage3"
       p:dataSource-ref="secondDatasource"
       p:persistenceUnitName="name2"
 
 >
     <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
     <property name="jpaProperties">
         <props>
             <prop key="hibernate.dialect">org.hibernate.dialect.MariaDBDialect</prop>
             <prop key="hibernate.show-sql">true</prop>
             <prop key="hibernate.jdbc.batch_size">10</prop>
         </props>
     </property>
 </bean>
 
 <!–     Transactions –>
 <bean id="mmEtcTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
     <property name="entityManagerFactory" ref="mmEtcEntityManager"/>
 </bean>
 
 <!– EntityManagerFactory –>
 <bean id="mmEntityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
       p:packagesToScan="pakage1, pakage2"
       p:dataSource-ref="datasource"
       p:persistenceUnitName="name2"
 
 >
     <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
     <property name="jpaProperties">
         <props>
             <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
             <prop key="hibernate.show-sql">true</prop>
             <prop key="hibernate.jdbc.batch_size">10</prop>
         </props>
     </property>
 </bean>
 
 <!– Transactions –>
 <bean id="mmTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
     <property name="entityManagerFactory" ref="mmEntityManager"/>
 </bean>
 
  • 0
  • Reply
Avatar
monkey Beginner
monkey Beginner
Em có thể paste cả exception lên đây không? (sử dụng thẻ pre nhé). Còn nhìn vào câu hỏi của em thì anh thấy lỗi này xảy ra khi trong code của em khai báo 2 đối tượng có cùng tên mmTransactionManager, em thử tìm kiếm xem có không và đặt 2 tên khác nhau nhé.
  • 0
  • Reply
Avatar
monkey Beginner
monkey Beginner
Spring JPA kết nối nhiều datasource sử dụng annotation nó sẽ kiểu thế này:
@Data
public class DataSourceProperties {
    private final String driverClassName;
    private final String jdbcUrl;
    private final String username;
    private final String password;
}

@ConfigurationProperties(prefix = "hibernate")
public class HibernateProperties extends Properties {
    private static final long serialVersionUID = -817908147155147987L;
}
  • 1
  • Reply
Avatar
monkey Beginner
monkey Beginner
The Best Answer
Khi sử dụng sẽ kiểu thế này:
import javax.sql.DataSource;

@Configuration
@EnableConfigurationProperties(HibernateProperties.class)
@ImportAutoConfiguration(RepositoryPropertiesConfig.class)
public class EzyJpaConfig {

    @Autowired
    private ApplicationContext appContext;

    public static final String EXAMPLE1 = "example1";
    public static final String EXAMPLE2 = "example2";

    @Bean
    public EzyDataSourceFactory repositoryDataSourceFactory(RepositoryProperties properties) {
        return new EzyDataSourceFactory(
            properties.getDataSources(),
            appContext.getEnvironment(),
            "spring.datasource.hikari"
        );
    }

    @Bean
    public EzyJpaRepositoryFactory repositoryJpaRepositoryFactory(
        EzyJpaTransactionManagerFactory repositoryJpaTransactionManagerFactory
    ) {
        return new EzyJpaRepositoryFactory(repositoryJpaTransactionManagerFactory);
    }

    @Bean
    public EzyEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory(
        EzyDataSourceFactory repositoryDataSourceFactory,
        HibernateProperties hibernateProperties
    ) {
        return new EzyEntityManagerFactoryFactory(
            repositoryDataSourceFactory,
            hibernateProperties,
            "com.example.ezy.web.entity"
        );
    }

    @Bean
    public EzyJpaTransactionManagerFactory repositoryJpaTransactionManagerFactory(
        EzyEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory
    ) {
        return new EzyJpaTransactionManagerFactory(repositoryEntityManagerFactoryFactory);
    }

    // ==================== example1 ==========
    @Bean
    public DataSource example1DataSource(EzyDataSourceFactory repositoryDataSourceFactory) {
        return repositoryDataSourceFactory.createDataSource(EXAMPLE1);
    }

    @Bean
    public EntityManagerFactory example1EntityManagerFactory(
        DataSource example1DataSource,
        EzyEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createEntityManagerFactory(
            EXAMPLE1,
            example1DataSource
        );
    }

    @Bean
    public JpaTransactionManager example1TransactionManager(
        EntityManagerFactory example1EntityManagerFactory,
        EzyJpaTransactionManagerFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createJpaTransactionManager(
            EXAMPLE1,
            example1EntityManagerFactory
        );
    }

    // ==================== example2 ==========
    @Bean
    public DataSource example2DataSource(EzyDataSourceFactory repositoryDataSourceFactory) {
        return repositoryDataSourceFactory.createDataSource(EXAMPLE2);
    }

    @Bean
    public EntityManagerFactory example2EntityManagerFactory(
        DataSource example2DataSource,
        EzyEntityManagerFactoryFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createEntityManagerFactory(
            EXAMPLE2,
            example2DataSource
        );
    }

    @Bean
    public JpaTransactionManager example2TransactionManager(
        EntityManagerFactory example2EntityManagerFactory,
        EzyJpaTransactionManagerFactory repositoryEntityManagerFactoryFactory
    ) {
        return repositoryEntityManagerFactoryFactory.createJpaTransactionManager(
            EXAMPLE2,
            example2EntityManagerFactory
        );
    }

    // ================= config ===========

    @Getter
    @ConstructorBinding
    @AllArgsConstructor
    @ConfigurationProperties(prefix = "jpa")
    public static class RepositoryProperties {
        private final Map dataSources;
    }

    @Configuration
    @EnableConfigurationProperties(RepositoryProperties.class)
    @ConditionalOnMissingBean(RepositoryProperties.class)
    public static class RepositoryPropertiesConfig {
    }
}
  • 1
  • Reply
Avatar
monkey Beginner
monkey Beginner
import javax.sql.DataSource;

public final class JpaConfigurations {
    private JpaConfigurations() {
    }

    public static  D createDataSource(
        DataSourceProperties properties,
        Class dataSourceClass
    ) {
        return DataSourceBuilder
            .create()
            .driverClassName(properties.getDriverClassName())
            .url(properties.getJdbcUrl())
            .username(properties.getUsername())
            .password(properties.getPassword())
            .type(dataSourceClass)
            .build();
    }

    public static EntityManagerFactory createEntityManagerFactory(
        DataSource dataSource,
        HibernateProperties hibernateProperties,
        String packageToScan
    ) {
        final LocalContainerEntityManagerFactoryBean factory =
            new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        factory.setPackagesToScan(packageToScan);
        factory.setDataSource(dataSource);
        factory.setJpaProperties(hibernateProperties);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    public static JpaTransactionManager createJpaTransactionManager(
        EntityManagerFactory entityManagerFactory
    ) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}
import javax.sql.DataSource;

@AllArgsConstructor
public class EzyDataSourceFactory {
    private final Map dataSourcePropertiesByChainId;
    private final Environment environment;
    private final String hikariDataSourcePropertyPrefix;

    public DataSource createDataSource(String datasourceId) {
        final HikariDataSource dataSource = JpaConfigurations.createDataSource(
            dataSourcePropertiesByChainId.get(datasourceId),
            HikariDataSource.class
        );
        final Binder binder = Binder.get(environment);
        binder.bind(hikariDataSourcePropertyPrefix, Bindable.ofInstance(dataSource));
        return dataSource;
    }
}
import javax.sql.DataSource;

@AllArgsConstructor
public class EzyEntityManagerFactoryFactory {

    private final EzyDataSourceFactory exampleDataSourceFactory;
    private final HibernateProperties hibernateProperties;
    private final String packageToScan;

    public EntityManagerFactory createEntityManagerFactory(String datasourceId) {
        return createEntityManagerFactory(datasourceId, null);
    }

    public EntityManagerFactory createEntityManagerFactory(
        String datasourceId,
        DataSource dataSource
    ) {
        return JpaConfigurations.createEntityManagerFactory(
            dataSource != null ? dataSource : exampleDataSourceFactory.createDataSource(datasourceId),
            hibernateProperties,
            packageToScan
        );
    }
}
@AllArgsConstructor
public class EzyJpaRepositoryFactory {
    private final EzyJpaTransactionManagerFactory ezyJpaTransactionManagerFactory;

    public <T, I, R extends JpaRepository> R createJpaRepository(
        String datasourceId,
        Class jpaRepositoryClass,
        JpaTransactionManager jpaTransactionManager
    ) {
        final JpaTransactionManager actualJpaTransactionManager =
            jpaTransactionManager == null
            ? jpaTransactionManager
            : ezyJpaTransactionManagerFactory.createJpaTransactionManager(datasourceId);
        final EntityManagerFactory entityManagerFactory =
            actualJpaTransactionManager.getEntityManagerFactory();
        assert entityManagerFactory != null;
        final EntityManager entityManager =
            SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
        final JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
        final TransactionInterceptor transactionInterceptor = new TransactionInterceptor(
            (TransactionManager) actualJpaTransactionManager,
            new AnnotationTransactionAttributeSource()
        );
        jpaRepositoryFactory.addRepositoryProxyPostProcessor((factory, repositoryInformation) ->
            factory.addAdvice(transactionInterceptor)
        );
        return jpaRepositoryFactory.getRepository(jpaRepositoryClass);
    }

    public <T, I, R extends JpaRepository> R createJpaRepositoryWithAspects(
        String datasourceId,
        Class jpaRepositoryClass,
        JpaTransactionManager jpaTransactionManager,
        Object... aspects
    ) {
        final R nakedJpaRepository = createJpaRepository(datasourceId, jpaRepositoryClass, jpaTransactionManager);
        final AspectJProxyFactory proxyFactory = new AspectJProxyFactory(nakedJpaRepository);
        for (Object aspect : aspects) {
            proxyFactory.addAspect(aspect);
        }
        return proxyFactory.getProxy();
    }
}
@AllArgsConstructor
public class EzyJpaTransactionManagerFactory {
    private final EzyEntityManagerFactoryFactory ezyEntryManagerFactoryFactory;

    public JpaTransactionManager createJpaTransactionManager(String datasourceId) {
        return createJpaTransactionManager(datasourceId, null);
    }

    public JpaTransactionManager createJpaTransactionManager(
        String datasourceId,
        EntityManagerFactory entityManagerFactory
    ) {
        return JpaConfigurations.createJpaTransactionManager(
            entityManagerFactory != null
                ? entityManagerFactory
                : ezyEntryManagerFactoryFactory.createEntityManagerFactory(datasourceId)
        );
    }
}
  • 1
  • Reply
Avatar
Minh Duc Cao Beginner
Lỗi này là do khai báo 2 bean có cùng một tên đó, kiểm tra lại file xml khai báo. Nếu sử dụng spring boot thì bạn tìm hiểu thêm anotation @Qualifier
  • 0
  • Reply