org.springframework.boot
spring-boot-starter-parent
2.1.2.RELEASE
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
首先在application.properties中添加数据源相关配置
,注意这里的url变成了jdbc-url。
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
创建一个Spring配置类,定义两个DataSource用来读取application.properties
中的不同配置。如下例子中,主数据源配置为spring.datasource.primary
开头的配置,第二数据源配置为spring.datasource.secondary
开头的配置。
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@ConfigurationProperties(prefix="spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@Qualifier("secondaryDataSource")
@Primary
@ConfigurationProperties(prefix="spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
下面分析一下DataSourceBuilder源码,DataSourceBuilder.create()的源码如下:
public static DataSourceBuilder> create() {
return new DataSourceBuilder<>(null);
}
public static DataSourceBuilder> create(ClassLoader classLoader) {
return new DataSourceBuilder<>(classLoader);
}
private DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader;
}
build()方法内容如下:
@SuppressWarnings("unchecked")
public T build() {
Class extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
第一行是获取DataSource实际类型,getType()源码:
private Class extends DataSource> getType() {
Class extends DataSource> type = (this.type != null) ? this.type
: findType(this.classLoader);
if (type != null) {
return type;
}
throw new IllegalStateException("No supported DataSource type found");
}
如果type未指定,则调用findType()方法:
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
"com.zaxxer.hikari.HikariDataSource",
"org.apache.tomcat.jdbc.pool.DataSource",
"org.apache.commons.dbcp2.BasicDataSource" };
@SuppressWarnings("unchecked")
public static Class extends DataSource> findType(ClassLoader classLoader) {
for (String name : DATA_SOURCE_TYPE_NAMES) {
try {
return (Class extends DataSource>) ClassUtils.forName(name,
classLoader);
}
catch (Exception ex) {
// Swallow and continue
}
}
return null;
}
从上面的代码中,我们可以看出默认寻找的是三个指定的Datasource类型,所以默认情况下如果存在第一个,那么返回的就是com.zaxxer.hikari.HikariDataSource类型,事实上Debug信息显示的确实如此:
回到build()方法中,第二行就是创建DataSource实例,不去深入。第三行maybeGetDriverClassName(),根据字面意思就是获取驱动类名称:
private void maybeGetDriverClassName() {
if (!this.properties.containsKey("driverClassName")
&& this.properties.containsKey("url")) {
String url = this.properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
this.properties.put("driverClassName", driverClass);
}
}
private void bind(DataSource result) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(
this.properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
Binder binder = new Binder(source.withAliases(aliases));
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
这个方法的第一行代码就告诉我们,需要指定的属性是driverClassName。如果没有这个属性,则会跳转到bind方法进行属性绑定。如果这里还没绑定成功,spring容器会去绑定。这里解释一下为啥使用driverClassName。这是因为使用的数据源是HikariDataSource,而这个数据源的属性就是driverClassName,jdbcUrl属性同理:
public class HikariDataSource extends HikariConfig implements DataSource, Closeable
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class);
private final AtomicBoolean isShutdown = new AtomicBoolean();
}
//类继承了HikariConfig
public class HikariConfig implements HikariConfigMXBean
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariConfig.class);
private static final char[] ID_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
private static final int DEFAULT_POOL_SIZE = 10;
private static boolean unitTest = false;
// Properties changeable at runtime through the HikariConfigMXBean
//
private volatile String catalog;
private volatile long connectionTimeout;
private volatile long validationTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
private volatile String username;
private volatile String password;
// Properties NOT changeable at runtime
//
private long initializationFailTimeout;
private String connectionInitSql;
private String connectionTestQuery;
private String dataSourceClassName;
private String dataSourceJndiName;
private String driverClassName;
private String jdbcUrl;
private String poolName;
private String schema;
private String transactionIsolationName;
private boolean isAutoCommit;
private boolean isReadOnly;
private boolean isIsolateInternalQueries;
private boolean isRegisterMbeans;
private boolean isAllowPoolSuspension;
private DataSource dataSource;
private Properties dataSourceProperties;
private ThreadFactory threadFactory;
private ScheduledExecutorService scheduledExecutor;
private MetricsTrackerFactory metricsTrackerFactory;
private Object metricRegistry;
private Object healthCheckRegistry;
private Properties healthCheckProperties;
....
}
新增primaryDataSource数据源的JPA配置,注意两处注释的地方,用于指定数据源对应的Entity
实体和Repository所在包路径
,用@Primary
区分主数据源。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactoryPrimary",
transactionManagerRef="transactionManagerPrimary",
basePackages= { "com.bo.entity.p" }) //设置Repository所在位置
public class PrimaryConfig {
@Resource(name= "primaryDataSource")
private DataSource primaryDataSource;
@Primary
@Bean(name = "entityManagerPrimary")
public EntityManager entityManager(FactoryBean entityManagerFactoryPrimary) throws Exception {
return entityManagerFactoryPrimary.getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary () throws Exception {
Map properties = new HashMap<>();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
properties.put("hibernate.show_sql", true);
properties.put("hibernate.format_sql", true);
properties.put("hibernate.hbm2ddl.auto", "update");// eg. validate | update | create | create-drop
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
// 设置JPA的EntityManagerFactory适配器
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
// 设置数据源
entityManagerFactoryBean.setDataSource(primaryDataSource);
// 设置实体类所在位置
entityManagerFactoryBean.setPackagesToScan("com.bo.entity.p");
// 设置Hibernate
entityManagerFactoryBean.getJpaPropertyMap().putAll(properties);
entityManagerFactoryBean.setPersistenceUnitName("primaryPersistenceUnit");
return entityManagerFactoryBean;
}
@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(FactoryBean entityManagerFactoryPrimary) throws Exception {
return new JpaTransactionManager(entityManagerFactoryPrimary.getObject());
}
}
新增secondaryDataSource据源的JPA配置,内容与第一数据源类似,具体如下:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactorySecondary",
transactionManagerRef="transactionManagerSecondary",
basePackages= { "com.bo.entity.s" }) //设置Repository所在位置
public class PrimaryConfig {
@Resource(name= "secondaryDataSource")
private DataSource secondaryDataSource;
@Primary
@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(FactoryBean entityManagerFactorySecondary) throws Exception {
return entityManagerFactorySecondary.getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary () throws Exception {
Map properties = new HashMap<>();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
properties.put("hibernate.show_sql", true);
properties.put("hibernate.format_sql", true);
properties.put("hibernate.hbm2ddl.auto", "update");// eg. validate | update | create | create-drop
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
// 设置JPA的EntityManagerFactory适配器
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
// 设置数据源
entityManagerFactoryBean.setDataSource(secondaryDataSource);
// 设置实体类所在位置
entityManagerFactoryBean.setPackagesToScan("com.bo.entity.s");
// 设置Hibernate
entityManagerFactoryBean.getJpaPropertyMap().putAll(properties);
entityManagerFactoryBean.setPersistenceUnitName("secondaryPersistenceUnit");
return entityManagerFactoryBean;
}
@Primary
@Bean(name = "transactionManagerSecondary")
public PlatformTransactionManager transactionManagerPrimary(FactoryBean entityManagerFactorySecondary) throws Exception {
return new JpaTransactionManager(entityManagerFactorySecondary.getObject());
}
}
补充一下:为了使用自定义数据源,需要将默认数据源自动配置排除在外,具体方法:
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
public class ShirodemoApplication {
public static void main(String[] args) {
SpringApplication.run(ShirodemoApplication.class, args);
}
}
Spring Boot多数据源配置与使用
springboot2.0为JPA定义多个默认数据源