场景 : 配置两个数据源,主数据源是mysql , 次数据源是impala [1] , 两个个数据源都使用spring-data-jpa 。
一、环境
- 基础组件版本
maven: 3.5.3
spring-boot: 2.0.1
spring-data: 2.0.5
jdk: 8
- 先把项目结构分成三部分,方便理解, 我们要做的就是用不同的持久层使用不同的数据源
src
├─持久层
│ ├─domain
│ └─repository
│
├─服务层
│ └─service
│
└─控制层
└─controller
- 如果使用多数据源,结构主要结构看起来如下,
服务层
和控制层
不变
src
├─mysql持久层
│ ├─domain
│ └─repository
│
├─impala持久层
│ ├─domain
│ └─repository
│
├─服务层
│ └─service
│
└─控制层
└─controller
二、配置多数据源
- 增加yaml配置
- mysql配置
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://100.100.100:3306/db_mysql?useUnicode=true&characterEncoding=utf-8&useSSL=false&zeroDateTimeBehavior=convertToNull&useNewIO=true&autoReconnectForPools=true
username: user
password: pwd
- impala配置 (
impala:
是自定义的,这个结构可以全部自定义)
impala:
datasource:
url: jdbc:hive2://finance-06:21050/db_impala;auth=noSasl;
username: user
password: pwd
- 分隔持久层,就是建两个不同的包,持久层分开放。
-
defaultDataSource
存放的是mysql的实体类和DAO ,impalaDataSource
放impala的DAO和实体类
- 配置datasource
- 新建
DefaultDBConfig
类,配置mysql的dataSource
、entityManagerFactory
和transactionManager
package com.demo.conf;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
// 【1】这里写的是DAO层的路径 ,如果你的DAO放在 com.xx.DAO下面,则这里写成 com.xx.DAO
basePackages = {"com.demo.defaultDataSource.repository"}
)
public class DefaultDBConfig {
@Autowired
private JpaProperties jpaProperties;
@Primary
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource") // 【2】datasource配置的前缀,对应上面 【mysql的yaml配置】
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("dataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("com.demo.defaultDataSource.domain") // 【3】这里是实体类的包路径
.persistenceUnit("defaultUnit") // 这里写成唯一的就可以了,具体我也不太明白 ,希望有人告知
// 【4】
.properties(jpaProperties.getHibernateProperties(new HibernateSettings()))
.build();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
注意上面标的【1】【2】【3】【4】这四个地方,
【1】:设置DAO所在的包路径
【2】:datasource的yaml配置前缀,默认数据原最好保持为spring.datasource
【3】:这里是实体类的包路径,里面是 @Entity 注解的实体,对应数据库的表。
【4】:因为这里自己生成数据源,没有spring-boot的自动配置了,所以要通过JpaProperties
获取默认配置并放进去,这里算是小重点
注意:这里配置的为默认数据源,记得加上@Primary
-
注意 : 图片中相同颜色和数字标注的地方要保持统一
图片是以前的老图,没有加@Primary
,在实际代码中记得加上
- 新建ImpalaDBConfig类,配置impala的dataSource、entityManagerFactory和transactionManager
package com.bbd.finance.xuanwu.web.conf;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "impalaEntityManagerFactory" ,
transactionManagerRef = "impalaTransactionManager" ,
basePackages = {"com.demo.impalaDataSource.dao"} // 【1】
)
public class ImpalaDBConfig {
@Autowired
private JpaProperties jpaProperties ;
@Bean(name = "impalaDataSource")
@ConfigurationProperties(prefix = "impala.datasource") // 【2】
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "impalaEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("impalaDataSource") DataSource dataSource , Environment environment
) {
LocalContainerEntityManagerFactoryBean impala = builder
.dataSource(dataSource)
.packages("com.demo.impalaDataSource.domain") // 【3】
.persistenceUnit("impala")
// 【4】
.properties(jpaProperties.getHibernateProperties(new HibernateSettings()))
.build();
return impala ;
}
@Bean(name = "impalaTransactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("impalaEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
- 要注意的地方和上面 mysql的一样。【2】标注的
impala.datasource
是自定义的前缀,可以自己定义,这里写成对应的就可以获取到配置 .- 配置完上面的就ok了,在spring-boot中,controller和service层都不用做什么修改(我的项目中全部用的 JPA)
可能会出现的问题
- 实体类中命名解析无效,比如实体类中的 userName , 没有自动映射成数据中的 user_name , 因为自己构建的数据源没有加载到 命名策略 , 此时请检查上面代码标注的第【4】点 , 可以在【4】标注的地方断点,看有没有加载
org.hibernate.boot.model.naming.SpringPhysicalNamingStrategy
1. 如果要实现mysql和oracle的多数据源,配置方法类似,把impala的配置改成oracle的就ok
2. 多数据源中默认数据源要加上@Primary
, (entityManagerFactory
, transactionManager
,dataSource
)
2018-11-30 更新 : 图片部分就不做更新了哈
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource") // 【2】datasource配置的前缀,对应上面 【mysql的yaml配置】
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
请把▲上面部分的代码换成▼下面的,这样可以解决报错
jdbcUrl is required with driverClassName
。原因是spring-boot 1.x
数据源使用的是spring.datasource.url
, 而spring-boot 2.x
使用的是spring.datasource.jdbcUrl
, 代码修改成下面的样子,可以自动兼容 。官方文档- 79.2 Configure Two DataSources
@Bean(name = "dataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(@Autowired @Qualifier("defaultDataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
@Bean(name="defaultDataSourceProperties")
@ConfigurationProperties("spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
参考:
- spring data jpa NamingStrategy
- Can't set JPA naming strategy after configuring multiple data sources (Spring 1.4.1 / Hibernate 5.x)
- Spring Boot多数据源配置与使用
- Using multiple datasources with Spring Boot and Spring Data ⇄ ⇄ [要翻墙]
- Using Multiple DataSources with Spring Boot and JPA
- Spring Boot Multiple Database Configuration Example
- Use Two EntityManagers
-
Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据 ↩