以前在介紹使用Spring-data-jpa時,都使用了單數據源。在單數據源的狀況下,Spring Boot的配置很是簡單,只須要在application.properties
文件中配置鏈接參數便可。可是每每隨着業務量發展,咱們一般會進行數據庫拆分或是引入其餘數據庫,從而咱們須要配置多個數據源,下面基於Spring-data-jpa的例子介紹多數據源的配置方式。java
1、項目結構圖(整體瀏覽一下最終完成後的建包樣式)mysql
具體展開圖(整體瀏覽一下最終完成後的建包樣式):web
2、建立一個Spring配置類,定義兩個DataSource用來讀取application.properties中
的不一樣配置。以下例子中,主數據源配置爲spring.datasource.primary
開頭的配置,第二數據源配置爲spring.datasource.secondary
開頭的配置。spring
【DataSourceConfig.java】sql
1 package com.didispace.config; 2 3 import org.springframework.beans.factory.annotation.Qualifier; 4 import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; 5 import org.springframework.boot.context.properties.ConfigurationProperties; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Configuration; 8 import org.springframework.context.annotation.Primary; 9 10 import javax.sql.DataSource; 11 12 @Configuration 13 public class DataSourceConfig { 14 15 @Bean(name = "primaryDataSource") 16 @Qualifier("primaryDataSource") 17 @Primary 18 @ConfigurationProperties(prefix="spring.datasource.primary") 19 public DataSource primaryDataSource() { 20 return DataSourceBuilder.create().build(); 21 } 22 23 @Bean(name = "secondaryDataSource") 24 @Qualifier("secondaryDataSource") 25 @ConfigurationProperties(prefix="spring.datasource.secondary") 26 public DataSource secondaryDataSource() { 27 return DataSourceBuilder.create().build(); 28 } 29 30 }
對應的application.properties
配置以下:數據庫
1 spring.datasource.primary.url=jdbc:mysql://localhost:3306/scott 2 spring.datasource.primary.username=root 3 spring.datasource.primary.password=root 4 spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver 5 6 spring.datasource.secondary.url=jdbc:mysql://localhost:3306/scott_2 7 spring.datasource.secondary.username=root 8 spring.datasource.secondary.password=root 9 spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver 10 11 #display sql in console,設置將每一條jpa指令都打印在控制檯中 12 spring.jpa.show-sql=true 13 14 spring.main.allow-bean-definition-overriding=true
3、數據源的JPA配置(幾個數據源,幾個相應名稱匹配的配置,本身理解,後期儘可能改爲批量方式)springboot
【PrimaryConfig.java】app
1 package com.didispace.config; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Qualifier; 5 import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; 6 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Configuration; 9 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 10 import org.springframework.orm.jpa.JpaTransactionManager; 11 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 12 import org.springframework.transaction.PlatformTransactionManager; 13 import org.springframework.transaction.annotation.EnableTransactionManagement; 14 15 import javax.persistence.EntityManager; 16 import javax.sql.DataSource; 17 import java.util.Map; 18 19 @Configuration 20 @EnableTransactionManagement 21 @EnableJpaRepositories( 22 entityManagerFactoryRef = "entityManagerFactoryPrimary", 23 transactionManagerRef = "transactionManagerPrimary", 24 basePackages = {"com.didispace.repository.primary"}) //設置Repository所在位置 25 public class PrimaryConfig { 26 27 @Autowired 28 @Qualifier("primaryDataSource") 29 private DataSource primaryDataSource; 30 31 @Bean(name = "entityManagerPrimary") 32 public EntityManager entityManager(EntityManagerFactoryBuilder builder) { 33 return entityManagerFactoryPrimary(builder).getObject().createEntityManager(); 34 } 35 36 @Bean(name = "entityManagerFactoryPrimary") 37 public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) { 38 return builder 39 .dataSource(primaryDataSource) 40 .properties(getVendorProperties(primaryDataSource)) 41 .packages("com.didispace.entity.primary") //設置實體類所在位置 42 .persistenceUnit("primaryPersistenceUnit") 43 .build(); 44 } 45 46 @Autowired 47 private JpaProperties jpaProperties; 48 49 private Map<String, String> getVendorProperties(DataSource dataSource) { 50 return jpaProperties.getHibernateProperties(dataSource); 51 } 52 53 @Bean(name = "transactionManagerPrimary") 54 public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) { 55 return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject()); 56 } 57 58 }
【SecondaryConfig.java】測試
1 package com.didispace.config; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Qualifier; 5 import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; 6 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Configuration; 9 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 10 import org.springframework.orm.jpa.JpaTransactionManager; 11 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 12 import org.springframework.transaction.PlatformTransactionManager; 13 import org.springframework.transaction.annotation.EnableTransactionManagement; 14 15 import javax.persistence.EntityManager; 16 import javax.sql.DataSource; 17 import java.util.Map; 18 19 @Configuration 20 @EnableTransactionManagement 21 @EnableJpaRepositories( 22 entityManagerFactoryRef = "entityManagerFactorySecondary", 23 transactionManagerRef = "transactionManagerSecondary", 24 basePackages = {"com.didispace.repository.secondary"}) //設置Repository所在位置 25 public class SecondaryConfig { 26 27 @Autowired 28 @Qualifier("secondaryDataSource") 29 private DataSource secondaryDataSource; 30 31 @Bean(name = "entityManagerSecondary") 32 public EntityManager entityManager(EntityManagerFactoryBuilder builder) { 33 return entityManagerFactorySecondary(builder).getObject().createEntityManager(); 34 } 35 36 @Bean(name = "entityManagerFactorySecondary") 37 public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) { 38 return builder 39 .dataSource(secondaryDataSource) 40 .properties(getVendorProperties(secondaryDataSource)) 41 .packages("com.didispace.entity.secondary") //設置實體類所在位置 42 .persistenceUnit("secondaryPersistenceUnit") 43 .build(); 44 } 45 46 @Autowired 47 private JpaProperties jpaProperties; 48 49 private Map<String, String> getVendorProperties(DataSource dataSource) { 50 return jpaProperties.getHibernateProperties(dataSource); 51 } 52 53 @Bean(name = "transactionManagerSecondary") 54 PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) { 55 return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject()); 56 } 57 58 }
4、最後,分別在這兩個包下建立各自的實體和數據訪問接口ui
測試以下:
編寫測試代碼:
1 package com.didispace; 2 3 import com.didispace.entity.secondary.Salgrade; 4 import com.didispace.repository.secondary.SalgradeRepository; 5 import com.didispace.entity.primary.Dept; 6 import com.didispace.repository.primary.DeptRepository; 7 import org.junit.Before; 8 import org.junit.Test; 9 import org.junit.runner.RunWith; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.boot.test.SpringApplicationConfiguration; 12 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 13 14 import java.util.Iterator; 15 import java.util.List; 16 17 18 @RunWith(SpringJUnit4ClassRunner.class) 19 @SpringApplicationConfiguration(Application.class) 20 public class ApplicationTests { 21 22 @Autowired 23 private DeptRepository deptRepository; 24 @Autowired 25 private SalgradeRepository salgradeRepository; 26 27 @Before 28 public void setUp() { 29 } 30 31 @Test 32 public void test() throws Exception { 33 34 35 System.out.println("--- --- --- 結果以下 --- --- ---"); 36 System.out.println("【數據庫scott】"); 37 List<Dept> ans = deptRepository.findByDname("FILL"); 38 Iterator<Dept> it = ans.iterator(); 39 while(it.hasNext()){ 40 Dept d = it.next(); 41 System.out.println(d); 42 43 } 44 45 System.out.println("--- --- --- --- --- --- --- --- "); 46 System.out.println("【數據庫scott_2】"); 47 Salgrade grade = salgradeRepository.findByGrade(1); 48 System.out.println(grade); 49 } 50 51 52 }
涉及的數據庫表:
輸出結果:
隨着Springboot升級到2.0,原來1.5.x的Jpa多數據源配置不能用了。如今總結一下Springboot2.0的jpa多數據源配置。
1、項目結構圖(整體瀏覽一下最終完成後的建包樣式)
2、建立一個Spring配置類,定義兩個DataSource用來讀取application.properties中
的不一樣配置。以下例子中,主數據源配置爲spring.datasource.primary
開頭的配置,第二數據源配置爲spring.datasource.secondary
開頭的配置。
【DataSourceConfig.java】
1 package com.angei.day0924jpa.Config; 2 3 import org.springframework.beans.factory.annotation.Qualifier; 4 import org.springframework.boot.context.properties.ConfigurationProperties; 5 import org.springframework.boot.jdbc.DataSourceBuilder; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Configuration; 8 import org.springframework.context.annotation.Primary; 9 10 import javax.sql.DataSource; 11 12 13 @Configuration 14 public class DataSourceConfig { 15 16 @Bean(name = "primaryDataSource") 17 @Qualifier("primaryDataSource") 18 @Primary 19 @ConfigurationProperties(prefix="spring.datasource.primary") 20 public DataSource primaryDataSource() { 21 return DataSourceBuilder.create().build(); 22 } 23 24 @Bean(name = "secondaryDataSource") 25 @Qualifier("secondaryDataSource") 26 @ConfigurationProperties(prefix="spring.datasource.secondary") 27 public DataSource secondaryDataSource() { 28 return DataSourceBuilder.create().build(); 29 } 30 31 }
對應的application.properties
配置以下:
1 # 多數據源配置 2 #primary 3 spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/scott?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8 4 spring.datasource.primary.username=root 5 spring.datasource.primary.password=root 6 spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver 7 8 #secondary 9 spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/scott_2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8 10 spring.datasource.secondary.username=root 11 spring.datasource.secondary.password=root 12 spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver 13 14 15 #display sql in console,設置將每一條jpa指令都打印在控制檯中 16 spring.jpa.show-sql=true 17 18 spring.main.allow-bean-definition-overriding=true
注意:
關於異常提示:
dataSource或dataSourceClassName或jdbcUrl是必需的!
解決方案:
將:
spring.datasource.primary.url=jdbc:mysql://IP地址/dbName
修改成:
spring.datasource.primary.jdbc-url=jdbc:mysql://IP地址/dbName
3、數據源的JPA配置(幾個數據源,幾個相應名稱匹配的配置,本身理解,後期儘可能改爲批量方式)
【PrimaryConfig.java】
1 package com.angei.day0924jpa.Config; 2 3 import org.springframework.beans.factory.annotation.Qualifier; 4 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.context.annotation.Primary; 8 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 9 import org.springframework.orm.jpa.JpaTransactionManager; 10 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 11 import org.springframework.transaction.PlatformTransactionManager; 12 import org.springframework.transaction.annotation.EnableTransactionManagement; 13 14 import javax.annotation.Resource; 15 import javax.persistence.EntityManager; 16 import javax.sql.DataSource; 17 import java.util.Properties; 18 19 20 @Configuration 21 @EnableTransactionManagement 22 @EnableJpaRepositories( 23 entityManagerFactoryRef = "entityManagerFactoryPrimary", 24 transactionManagerRef = "transactionManagerPrimary", 25 basePackages = {"com.angei.day0924jpa.repository.primary"}) //設置Repository所在位置 26 27 public class PrimaryConfig { 28 29 @Resource 30 @Qualifier("primaryDataSource") 31 private DataSource primaryDataSource; 32 33 @Primary 34 @Bean(name = "entityManagerPrimary") 35 public EntityManager entityManager(EntityManagerFactoryBuilder builder) { 36 return entityManagerFactoryPrimary(builder).getObject().createEntityManager(); 37 } 38 39 @Resource 40 private Properties jpaProperties; 41 42 43 @Primary 44 @Bean(name = "entityManagerFactoryPrimary") 45 public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) { 46 LocalContainerEntityManagerFactoryBean entityManagerFactory = builder 47 .dataSource(primaryDataSource) 48 .packages("com.angei.day0924jpa.entity.primary") //設置實體類所在位置 49 .persistenceUnit("primaryPersistenceUnit") 50 .build(); 51 entityManagerFactory.setJpaProperties(jpaProperties); 52 return entityManagerFactory; 53 } 54 55 @Primary 56 @Bean(name = "transactionManagerPrimary") 57 public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) { 58 return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject()); 59 } 60 61 }
【SecondaryConfig.java】
1 package com.angei.day0924jpa.Config; 2 3 import org.springframework.beans.factory.annotation.Qualifier; 4 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 8 import org.springframework.orm.jpa.JpaTransactionManager; 9 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 10 import org.springframework.transaction.PlatformTransactionManager; 11 import org.springframework.transaction.annotation.EnableTransactionManagement; 12 13 import javax.annotation.Resource; 14 import javax.persistence.EntityManager; 15 import javax.sql.DataSource; 16 import java.util.Properties; 17 18 19 @Configuration 20 @EnableTransactionManagement 21 @EnableJpaRepositories( 22 entityManagerFactoryRef = "entityManagerFactorySecondary", 23 transactionManagerRef = "transactionManagerSecondary", 24 basePackages = {"com.angei.day0924jpa.repository.secondary"}) //設置Repository所在位置 25 public class SecondaryConfig { 26 27 @Resource 28 @Qualifier("secondaryDataSource") 29 private DataSource secondaryDataSource; 30 31 @Resource 32 private Properties jpaProperties; 33 34 35 @Bean(name = "entityManagerSecondary") 36 public EntityManager entityManager(EntityManagerFactoryBuilder builder) { 37 return entityManagerFactorySecondary(builder).getObject().createEntityManager(); 38 } 39 40 @Bean(name = "entityManagerFactorySecondary") 41 public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) { 42 LocalContainerEntityManagerFactoryBean entityManagerFactory 43 = builder 44 .dataSource(secondaryDataSource) 45 .packages("com.angei.day0924jpa.entity.secondary") //設置實體類所在位置 46 .persistenceUnit("secondaryPersistenceUnit")//持久化單元建立一個默認便可,多個便要分別命名 47 .build(); 48 entityManagerFactory.setJpaProperties(jpaProperties); 49 return entityManagerFactory; 50 } 51 52 @Bean(name = "transactionManagerSecondary") 53 public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) { 54 return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject()); 55 } 56 }
說明:從SpringBoot1.x 升級至SpringBoot2.1.x 上述配置作了以下的修改:
4、最後,分別在這兩個包下建立各自的實體和數據訪問接口
測試以下:
編寫測試代碼:
【ContractController.java】
1 package com.angei.day0924jpa.controller; 2 3 import com.angei.day0924jpa.entity.primary.Dept; 4 import com.angei.day0924jpa.entity.secondary.Salgrade; 5 import com.angei.day0924jpa.repository.primary.DeptRepository; 6 import com.angei.day0924jpa.repository.secondary.SalgradeRepository; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.ResponseBody; 11 12 import java.util.Iterator; 13 import java.util.List; 14 15 @Controller 16 @RequestMapping("/contract") 17 public class ContractController { 18 19 @Autowired 20 private DeptRepository deptRepository; 21 22 @Autowired 23 private SalgradeRepository salgradeRepository; 24 25 @RequestMapping("/testing") 26 @ResponseBody 27 public void ContractQuery(){ 28 System.out.println("【數據庫1】"); 29 List<Dept> ans = deptRepository.findByDname("FILL"); 30 Iterator<Dept> it = ans.iterator(); 31 while(it.hasNext()){ 32 33 Dept d = it.next(); 34 System.out.println(d); 35 } 36 System.out.println("【數據庫2】"); 37 Salgrade res = salgradeRepository.findByGrade(2); 38 System.out.println(res); 39 } 40 }
訪問:http://localhost:8080/contract/testing
顯示以下: