Spring Boot Configure and Use Two DataSources

后端 未结 9 2038
不思量自难忘°
不思量自难忘° 2020-11-22 04:26

How can I configure and use two data sources?

For example here is what I have for the first data source:

application.properties



        
相关标签:
9条回答
  • 2020-11-22 04:55

    I also had to setup connection to 2 datasources from Spring Boot application, and it was not easy - the solution mentioned in the Spring Boot documentation didn't work. After a long digging through the internet I made it work and the main idea was taken from this article and bunch of other places.

    The following solution is written in Kotlin and works with Spring Boot 2.1.3 and Hibernate Core 5.3.7. Main issue was that it was not enough just to setup different DataSource configs, but it was also necessary to configure EntityManagerFactory and TransactionManager for both databases.

    Here is config for the first (Primary) database:

    @Configuration
    @EnableJpaRepositories(
        entityManagerFactoryRef = "firstDbEntityManagerFactory",
        transactionManagerRef = "firstDbTransactionManager",
        basePackages = ["org.path.to.firstDb.domain"]
    )
    @EnableTransactionManagement
    class FirstDbConfig {
    
        @Bean
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource.firstDb")
        fun firstDbDataSource(): DataSource {
            return DataSourceBuilder.create().build()
        }
    
        @Primary
        @Bean(name = ["firstDbEntityManagerFactory"])
        fun firstDbEntityManagerFactory(
            builder: EntityManagerFactoryBuilder,
            @Qualifier("firstDbDataSource") dataSource: DataSource
        ): LocalContainerEntityManagerFactoryBean {
            return builder
                .dataSource(dataSource)
                .packages(SomeEntity::class.java)
                .persistenceUnit("firstDb")
                // Following is the optional configuration for naming strategy
                .properties(
                    singletonMap(
                        "hibernate.naming.physical-strategy",
                        "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"
                    )
                )
                .build()
        }
    
        @Primary
        @Bean(name = ["firstDbTransactionManager"])
        fun firstDbTransactionManager(
            @Qualifier("firstDbEntityManagerFactory") firstDbEntityManagerFactory: EntityManagerFactory
        ): PlatformTransactionManager {
            return JpaTransactionManager(firstDbEntityManagerFactory)
        }
    }
    

    And this is config for second database:

    @Configuration
    @EnableJpaRepositories(
        entityManagerFactoryRef = "secondDbEntityManagerFactory",
        transactionManagerRef = "secondDbTransactionManager",
        basePackages = ["org.path.to.secondDb.domain"]
    )
    @EnableTransactionManagement
    class SecondDbConfig {
    
        @Bean
        @ConfigurationProperties("spring.datasource.secondDb")
        fun secondDbDataSource(): DataSource {
            return DataSourceBuilder.create().build()
        }
    
        @Bean(name = ["secondDbEntityManagerFactory"])
        fun secondDbEntityManagerFactory(
            builder: EntityManagerFactoryBuilder,
            @Qualifier("secondDbDataSource") dataSource: DataSource
        ): LocalContainerEntityManagerFactoryBean {
            return builder
                .dataSource(dataSource)
                .packages(EntityFromSecondDb::class.java)
                .persistenceUnit("secondDb")
                .build()
        }
    
        @Bean(name = ["secondDbTransactionManager"])
        fun secondDbTransactionManager(
            @Qualifier("secondDbEntityManagerFactory") secondDbEntityManagerFactory: EntityManagerFactory
        ): PlatformTransactionManager {
            return JpaTransactionManager(secondDbEntityManagerFactory)
        }
    }
    

    The properties for datasources are like this:

    spring.datasource.firstDb.jdbc-url=
    spring.datasource.firstDb.username=
    spring.datasource.firstDb.password=
    
    spring.datasource.secondDb.jdbc-url=
    spring.datasource.secondDb.username=
    spring.datasource.secondDb.password=
    

    Issue with properties was that I had to define jdbc-url instead of url because otherwise I had an exception.

    p.s. Also you might have different naming schemes in your databases, which was the case for me. Since Hibernate 5 does not support all previous naming schemes, I had to use solution from this answer - maybe it will also help someone as well.

    0 讨论(0)
  • 2020-11-22 05:01

    Here is the Complete solution

    #First Datasource (DB1)
    db1.datasource.url: url
    db1.datasource.username:user
    db1.datasource.password:password
    
    #Second Datasource (DB2)
    db2.datasource.url:url
    db2.datasource.username:user
    db2.datasource.password:password
    

    Since we are going to get access two different databases (db1, db2), we need to configure each data source configuration separately like:

    public class DB1_DataSource {
    @Autowired
    private Environment env;
    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean db1EntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(db1Datasource());
        em.setPersistenceUnitName("db1EntityManager");
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<string, object=""> properties = new HashMap<>();
        properties.put("hibernate.dialect",
                env.getProperty("hibernate.dialect"));
        properties.put("hibernate.show-sql",
                env.getProperty("jdbc.show-sql"));
        em.setJpaPropertyMap(properties);
        return em;
    }
    
    @Primary
    @Bean
    public DataSource db1Datasource() {
    
        DriverManagerDataSource dataSource
                = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                env.getProperty("jdbc.driver-class-name"));
        dataSource.setUrl(env.getProperty("db1.datasource.url"));
        dataSource.setUsername(env.getProperty("db1.datasource.username"));
        dataSource.setPassword(env.getProperty("db1.datasource.password"));
    
        return dataSource;
    }
    
    @Primary
    @Bean
    public PlatformTransactionManager db1TransactionManager() {
    
        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                db1EntityManager().getObject());
        return transactionManager;
    }
    }
    

    Second Datasource :

    public class DB2_DataSource {
    
    @Autowired
    private Environment env;
    
    @Bean
    public LocalContainerEntityManagerFactoryBean db2EntityManager() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(db2Datasource());
        em.setPersistenceUnitName("db2EntityManager");
        HibernateJpaVendorAdapter vendorAdapter
                = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<string, object=""> properties = new HashMap<>();
        properties.put("hibernate.dialect",
                env.getProperty("hibernate.dialect"));
        properties.put("hibernate.show-sql",
                env.getProperty("jdbc.show-sql"));
        em.setJpaPropertyMap(properties);
        return em;
    }
    
    @Bean
    public DataSource db2Datasource() {
        DriverManagerDataSource dataSource
                = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                env.getProperty("jdbc.driver-class-name"));
        dataSource.setUrl(env.getProperty("db2.datasource.url"));
        dataSource.setUsername(env.getProperty("db2.datasource.username"));
        dataSource.setPassword(env.getProperty("db2.datasource.password"));
    
        return dataSource;
    }
    
    @Bean
    public PlatformTransactionManager db2TransactionManager() {
        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                db2EntityManager().getObject());
        return transactionManager;
    }
    }
    

    Here you can find the complete Example on my blog : Spring Boot with Multiple DataSource Configuration

    0 讨论(0)
  • 2020-11-22 05:02

    @Primary annotation when used against a method like below works good if the two data sources are on the same db location/server.

    @Bean(name = "datasource1")
    @ConfigurationProperties("database1.datasource")
    @Primary
    public DataSource dataSource(){
      return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "datasource2")
    @ConfigurationProperties("database2.datasource")
    public DataSource dataSource2(){
      return DataSourceBuilder.create().build();
    }
    

    If the data sources are on different servers its better to use @Component along with @Primary annotation. The following code snippet works well on two different data sources at different locations

    database1.datasource.url = jdbc:mysql://127.0.0.1:3306/db1
    database1.datasource.username = root
    database1.datasource.password = mysql
    database1.datasource.driver-class-name=com.mysql.jdbc.Driver
    
    database2.datasource1.url = jdbc:mysql://192.168.113.51:3306/db2
    database2.datasource1.username = root
    database2.datasource1.password = mysql
    database2.datasource1.driver-class-name=com.mysql.jdbc.Driver
    
    @Configuration
    @Primary
    @Component
    @ComponentScan("com.db1.bean")
    class DBConfiguration1{
        @Bean("db1Ds")
        @ConfigurationProperties(prefix="database1.datasource")
        public DataSource primaryDataSource() {
            return DataSourceBuilder.create().build();
        }
    
    }
    
    @Configuration
    @Component
    @ComponentScan("com.db2.bean")
    class DBConfiguration2{
        @Bean("db2Ds")
        @ConfigurationProperties(prefix="database2.datasource1")
        public DataSource primaryDataSource() {
            return DataSourceBuilder.create().build();
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 05:05

    Here you go.

    Add in your application.properties file:

    #first db
    spring.datasource.url = [url]
    spring.datasource.username = [username]
    spring.datasource.password = [password]
    spring.datasource.driverClassName = oracle.jdbc.OracleDriver
    
    #second db ...
    spring.secondDatasource.url = [url]
    spring.secondDatasource.username = [username]
    spring.secondDatasource.password = [password]
    spring.secondDatasource.driverClassName = oracle.jdbc.OracleDriver
    

    Add in any class annotated with @Configuration the following methods:

    @Bean
    @Primary
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties(prefix="spring.secondDatasource")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    0 讨论(0)
  • 2020-11-22 05:05
    # Here '1stDB' is the database name
    spring.datasource.url=jdbc:mysql://localhost/A
    spring.datasource.username=root
    spring.datasource.password=root
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    
    
    # Here '2ndDB' is the database name
    spring.second-datasourcee.url=jdbc:mysql://localhost/B
    spring.second-datasource.username=root
    spring.second-datasource.password=root
    spring.second-datasource.driver-class-name=com.mysql.jdbc.Driver
    
    
        @Bean
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource firstDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.second-datasource")
        public DataSource secondDataSource() {
           return DataSourceBuilder.create().build();
        }
    
    0 讨论(0)
  • 2020-11-22 05:14

    Refer the official documentation


    Creating more than one data source works same as creating the first one. You might want to mark one of them as @Primary if you are using the default auto-configuration for JDBC or JPA (then that one will be picked up by any @Autowired injections).

    @Bean
    @Primary
    @ConfigurationProperties(prefix="datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties(prefix="datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    0 讨论(0)
提交回复
热议问题