【问题标题】:Connection to one from two Identical databases in Spring boot application at RUNTIME在运行时连接到 Spring Boot 应用程序中两个相同数据库中的一个
【发布时间】:2019-05-06 22:25:08
【问题描述】:

更新

当我尝试连接到 2 个相同的数据库(具有相同的表)然后选择其中一个来发出请求时,我遇到了 Spring 框架的问题。我为此使用了 3 个配置文件 DbConfigAbstractRoutingDataSource 和一个 application.properties 文件。 我目前在启动我的应用程序时没有收到任何错误,但是每当我使用我的 restful 网络服务时,我都会得到空数据,请检查下面的控制器文件。

注意:我已经关注了所有这些链接,但仍然没有结果:

  1. Baeldung AbstractRoutingDataSource guide
  2. Alexander guide
  3. Dynamic DataSource Routing on Spring website

我正在开发 Spring Boot v2.0.5。

注意:如果您对 AbstractRoutingDataSource 类及其工作原理没有任何想法,请不要回答我。

我的 application.properties 文件

spring.jpa.database=mysql
spring.jpa.open-in-view=false

# Main database DEV_database1 
first.datasource.url = jdbc:mysql://localhost:3306/DEV_database1 
first.datasource.username = root 
first.datasource.password =  
first.datasource.driver-class-name = com.mysql.jdbc.Driver

# second database DEV_database2 
second.datasource.url = jdbc:mysql://localhost:3306/DEV_database2 
second.datasource.username = root 
second.datasource.password =  
second.datasource.driver-class-name = com.mysql.jdbc.Driver

spring.jpa.database-platform = org.hibernate.dialect.H2Dialect
spring.jpa.show-sql = true
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#...

[更新]我的 DbConfig 文件

@Configuration
public class DbConfig {

    @Bean
    @Primary
    @ConfigurationProperties("first.datasource")
    public DataSourceProperties firstDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    public HikariDataSource firstDataSource() {
        return firstDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("second.datasource")
    public DataSourceProperties secondDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public HikariDataSource secondDataSource() {
        return secondDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }   

    @Bean
    DataSource dataSources() {

        AbstractRoutingDataSource dataSource = new CustomerRoutingDataSource();

        Map<Object, Object> resolvedDataSources = new HashMap<>();    
        resolvedDataSources.put(DbType.DATASOURCE1, firstDataSource());
        resolvedDataSources.put(DbType.DATASOURCE2, secondDataSource());

        dataSource.setDefaultTargetDataSource(firstDataSource()); // << default
        dataSource.setTargetDataSources(resolvedDataSources);
        return dataSource;
    }   
}

我的 CustomerRoutingDataSource.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * AbstractRoutingDatasource can be used in place of standard DataSource implementations and enables a mechanism to
 * determine which concrete DataSource to use for each operation at runtime.
 * 
 * @author fre
 */
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {

    private static final Logger log = LoggerFactory.getLogger(CustomerRoutingDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
        log.info(">>> determineCurrentLookupKey thread: {}", Thread.currentThread().getName() );
        log.info(">>> RoutingDataSource: {}", DbContextHolder.getDbType());
        return DbContextHolder.getDbType();
    }
}

我的 DbContextHolder.java

public class DbContextHolder {

       private static final ThreadLocal<DbType> contextHolder =
                new ThreadLocal<DbType>();
       // set the datasource
       public static void setDbType(DbType dbType) {
           if(dbType == null){
               throw new NullPointerException();
           }
          contextHolder.set(dbType);
       }
       // get the current datasource in use
       public static DbType getDbType() {
          return (DbType) contextHolder.get();
       }
       // clear datasource
       public static void clearDbType() {
          contextHolder.remove();
       }
}

DbType.java

public enum DbType {
    DATASOURCE1, 
    DATASOURCE2,
}

这就是我在 Controller 中使用它的方式:

我的控制器

@RestController
@RequestMapping("/api/user")
public class MembreController extends MainController {

    @Autowired
    MembreRepository membreRepository;

    @GetMapping("/login")
    public ResponseString login(String userName, String password) {

        // setting the datasouce to DATASOURCE1
        DbContextHolder.setDbType(DbType.DATASOURCE1);

        // finding the list of user in DataSource1
        List<Membres> list = membreRepository.findAll();

        // clearing the current datasource
        DbContextHolder.clearDbType();

        for (Iterator<Membres> membreIter = list.iterator(); membreIter.hasNext();) {
            Membres membre = membreIter.next();

            if (membre.getUserName().equals(userName) && membre.getPassword().equals(password)) {
                return new ResponseString("Welcome" + membre.getFirstName() + " " + membre.getLastName());
            }
        }
        return new ResponseString("User not found");
    }

}

我的 Application.java 文件

@ComponentScan
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    /**
     * main method
     * 
     * @param args
     */
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }


    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

}

【问题讨论】:

  • 什么不起作用?你得到什么错误?此外,您正在使用 Spring Boot,那么您将如何加载该 XML 配置文件。看起来你正在混合你不应该混合的东西(或者至少以错误的方式混合它们)。
  • 我实际上没有收到任何错误,您建议我将xml文件中的配置更改为java文件吗?
  • 不,我想知道您为什么使用 XML 和 Spring Boot。我还怀疑这是通过(重新)定义事物而不是使用 Spring Boot 来解决问题的一部分。此外,我建议将spring.jpa.open-in-view=false 添加到您的属性中,如果您没有在您有时间为AbstractRoutingDataSource 设置正确值之前很早就打开EntitymanagerConnection。还从您的 xml 中删除 application.properties 的加载,因为这应该由 Spring Boot 处理。此外,您的 spring.jpa 属性也没有用,因为您正在自己配置 JPA。
  • 好的 appContext.xml 文件呢?我必须删除它吗?
  • 我照你说的做了,没有任何改变:(

标签: spring-boot jpa datasource multiple-databases spring-config


【解决方案1】:

好吧,我找到了解决方案! 问题只是我的 .properties 和我的 DbConfig 文件。我只是从 .properties 文件中省略了数据库参数,并将它们手动添加到我的 DbConfig 文件中。它工作正常:

我的属性文件

spring.jpa.database=mysql
spring.jpa.open-in-view=false

spring.jpa.database-platform = org.hibernate.dialect.H2Dialect
spring.jpa.show-sql = true
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#...

我的数据库配置

   @Configuration
public class DbConfig{

    @Autowired
    private Environment env;

    @Bean(name = "dataSource1")
    DataSource dataSource1() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/DEV_database1?zeroDateTimeBehavior=convertToNull")
                .driverClassName("com.mysql.jdbc.Driver").username("root").password("").build();
    }

    @Bean(name = "dataSource2")
    DataSource dataSource2() {
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/database2")
                .driverClassName("com.mysql.jdbc.Driver").username("root").password("").build();
    }

    @Primary    
    @Bean(name = "mainDataSource")
    DataSource dataSources() {

        AbstractRoutingDataSource dataSource = new CustomerRoutingDataSource();
        DataSource dataSource1= dataSource1();
        DataSource dataSource2 = dataSource2();
        Map<Object, Object> resolvedDataSources = new HashMap<>();
        resolvedDataSources.put(DbType.DATASOURCE1, dataSource1);
        resolvedDataSources.put(DbType.DATASOURCE2, dataSource2 );
        dataSource.setTargetDataSources(resolvedDataSources);
        dataSource.setDefaultTargetDataSource(dataSource1); // << default
        return dataSource;
    }
}

这不是全部!在此之后我遇到了一个新错误!它说:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

   org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration
┌─────┐
|  dataSource defined in class path resource [tn/fre/gestdoc/DbConfig.class]
↑     ↓
|  dataSource1 defined in class path resource [tn/fre/gestdoc/DbConfig.class]
↑     ↓
|  org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
└─────┘

我跟着这个:Answer on GitHub 并且错误消失了,应用程序现在可以正常工作了!!我可以从我的登录控制器中选择哪个数据源,谢谢@M. Deinum

【讨论】:

    猜你喜欢
    • 2023-01-15
    • 1970-01-01
    • 2020-05-04
    • 2017-06-11
    • 2018-11-08
    • 2017-02-23
    • 2022-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多