背景
在项目中有时候需要同时连接多个数据库,由于一般数据库的连接都是通过数据源连接池去连接的,所以该问题就是多数据源的配置问题。下面通过Spring Boot Jpa框架说明多数据源的配置方式及要点。
第一步创建DataSource
其实DataSource的创建并不复杂,首先准备两个数据库test1、test2,他们分别包含t_product、t_user
application.properties配置如下
app.datasource.test1.jdbc-url= jdbc:mysql://127.0.0.1:3306/test1
app.datasource.test1.username= root
app.datasource.test1.password=
app.datasource.test2.jdbc-url= jdbc:mysql://127.0.0.1:3306/test2
app.datasource.test2.username= root
app.datasource.test2.password=
由于是多数据源而数据源又是为jpa服务的,所以在dao层的包结构下将不同数据库的实体分开放置,这样的作用后面再说
@Data
@Entity
@Table(name = "t_product")
public class ProductDO {
@Id
@GeneratedValue
private Long id;
private String productName;
}
public interface ProductRepository extends JpaRepository<ProductDO, Long> {}
@Data
@Entity
@Table(name = "t_user")
public class UserDO {
@Id
@GeneratedValue
private Long id;
private String userName;
}
public interface UserRepository extends JpaRepository<UserDO,Long> {}
Test1Config通过@ConfigurationProperties
注解,自动读取application.properties中app.datasource.test1开头的配置项注入到该类中,由于该类是继承的HikariConfig,所以实际是注入到了HikariConfig
@Configuration
@ConfigurationProperties("app.datasource.test1")
public class Test1Config extends HikariConfig {
@Bean
public DataSource test1DataSource(){
return new HikariDataSource(this);
}
}
查看HikariConfig的代码里面就有相关的属性
Test2Config同理,需要注意将test1替换为test2。这时启动项目就能看到两个DataSource创建了
2019-01-21 09:59:43.554 INFO 63713 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-01-21 09:59:43.816 INFO 63713 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-01-21 09:59:43.827 INFO 63713 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
2019-01-21 09:59:43.835 INFO 63713 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
第二步创建EntityManagerFactory
在jdbc中有数据源就可以工作了,但在Jpa中还需要EntityManagerFactory
,那么第二步就是创建EntityManagerFactory
,关于EntityManagerFactory
的相关概念可以看之前的博客Spring Data之EntityManagerFactory创建及源码分析
@Configuration
@ConfigurationProperties("app.datasource.test1")
public class Test1Config extends HikariConfig {
@Bean
public DataSource test1DataSource(){
return new HikariDataSource(this);
}
@Bean
public LocalContainerEntityManagerFactoryBean test1EntityManagerFactory() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
//设置数据源
factoryBean.setDataSource(test1DataSource());
//设置jpa相关参数
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
//设置该EntityManagerFactory需要扫描的包路径
//这里就是将dao层的包结构下将不同数据库的实体分开放置的好处
//意思是该包下的实体操作都是有该EntityManagerFactory处理
factoryBean.setPackagesToScan(Test1Config.class.getPackage().getName());
factoryBean.setPersistenceUnitName("test1");
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.physical_naming_strategy","org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
factoryBean.setJpaPropertyMap(properties);
return factoryBean;
}
}
其中hibernate.physical_naming_strategy:org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
设置的作用是告知jpa实体与sql的映射使用驼峰命名法,即java代码中的productName
属性在生成sql时将转换为product_name
第三步创建TransactionManager
在jpa中事务是通过EntityManagerFactory
获取的,因为是不同的EntityManagerFactory
所以也要创建不同的TransactionManager
@Bean
PlatformTransactionManager test1TransactionManager() {
return new JpaTransactionManager(test1EntityManagerFactory().getObject());
}
第四步开启JPA
在Sping Boot中要启用JPA除了引用jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
还需要通过@EnableJpaRepositories
注解开启JPA相关的初始化,由于这里我们是手动创建的EntityManagerFactory
和TransactionManager
,所以需要手动设置@EnableJpaRepositories
的参数,以下的完整的代码
@Configuration
@ConfigurationProperties("app.datasource.test1")
@EnableJpaRepositories(
entityManagerFactoryRef = "test1EntityManagerFactory",
transactionManagerRef = "test1TransactionManager")
public class Test1Config extends HikariConfig {
@Bean
public DataSource test1DataSource(){
return new HikariDataSource(this);
}
@Bean
public LocalContainerEntityManagerFactoryBean test1EntityManagerFactory() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(test1DataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan(Test1Config.class.getPackage().getName());
factoryBean.setPersistenceUnitName("test1");
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.physical_naming_strategy","org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
factoryBean.setJpaPropertyMap(properties);
return factoryBean;
}
@Bean
PlatformTransactionManager test1TransactionManager() {
return new JpaTransactionManager(test1EntityManagerFactory().getObject());
}
}
启动项目可以看到控制台输出,创建了两个数据源以及test1、test2的PersistenceUnitInfo
2019-01-21 10:24:23.416 INFO 63865 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-01-21 10:24:23.776 INFO 63865 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-01-21 10:24:23.799 INFO 63865 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: test1
...]
2019-01-21 10:24:23.840 INFO 63865 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.7.Final}
2019-01-21 10:24:23.841 INFO 63865 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2019-01-21 10:24:23.925 INFO 63865 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-01-21 10:24:24.018 INFO 63865 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
2019-01-21 10:24:24.162 INFO 63865 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'test1'
2019-01-21 10:24:24.173 INFO 63865 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
2019-01-21 10:24:24.180 INFO 63865 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
2019-01-21 10:24:24.182 INFO 63865 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: test2
...]
2019-01-21 10:24:24.185 INFO 63865 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
2019-01-21 10:24:24.195 INFO 63865 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'test2'
验证
测试一下分别往两个表插入一条数据
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private ProductRepository productRepository;
@Autowired
private UserRepository userRepository;
@Test
public void test() {
ProductDO productDO = new ProductDO();
productDO.setProductName("苹果");
productRepository.save(productDO);
UserDO userDO = new UserDO();
userDO.setUserName("张三");
userRepository.save(userDO);
}
}
可以看到数据被正确插入对应的数据库
总结
网上有很多创建多数据源的文章,但都不尽相同。不过只要明白了套路(步骤)就可以变化写法定制自己需要的多数据源形式。