springboot2.0下为JPA定义多个默认数据源

 

注意标题:这里是定义多个默认类型的数据源,不是引用了druid等其余的DataSourcejava

环境:mysql

这里直接贴pom文件的内容:web

引入的springboot为:spring

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--mysql-connector-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>

这里使用的是springboot的版本是2.1.2.RELEASE,这里特地标注出版本,是想说版本不一样代码确定会有差别,配置也会随之不一样。(至少咱们在开发过程当中的代码、文档也会有版本管理吧)sql

在springboot中,默认的dataSource根本不须要开发人员再单独写代码配置,只须要在application.properties文件中添加如下内容便可:apache

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

配置完成,启动springboot就可使用这个数据源了。tomcat

在咱们作项目过程当中,单一数据源开始不能知足咱们的须要了,须要配置多个数据源,那么该怎么操做呢?springboot

首先,仍是须要在application.properties文件中书写相关配置,与默认的配置略有差别,好比我这里配置的两个数据源:app

spring.datasource.secondary.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbcUrl=jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&characterEncoding=utf8
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456

spring.datasource.primary.jdbcUrl=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driverClassName=com.mysql.cj.jdbc.Driver

说明:spring-boot

数据源的配置仍是写在spring.datasource前缀以后,不一样的数据源使用用于区别的名称,例如:primary、secondary等等,在名称后面再追加驱动类名、用户名、密码、url等内容。须要注意的是这里的驱动类名与默认的状况不一样,默认采用的是driver-class-name,而这里使用的是driverClassName。另外url参数名也有变化,由原来的url变成了jdbcUrl(为啥不一样稍后介绍)

配置完成了,那么接下来就开始编写自定义配置类(springboot是代码配置咯),先贴代码,而后解释:

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@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")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource(){
        return DataSourceBuilder.create().build();
    }
}

接触过springboot的基本均可以看懂这个代码,须要注意的是@ConfigurationProperties这个注解,标明了前缀。另一个须要看的就是定义的两个数据源,使用的代码倒是同样的:

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;
	}

这里没什么特别的地方,是告诉DataSourceBuilder使用的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");
	}

若是未制定,则调用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,因此。。。不解释了。简单贴一下HikariDataSource的源码:

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;
....
}

看到了吧?这里有driverClassName

 

补充一下:为了使用自定义数据源,须要将默认数据源自动配置排除在外,具体方法:

@SpringBootApplication(exclude = {
        DataSourceAutoConfiguration.class
})
@EnableTransactionManagement
public class ShirodemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShirodemoApplication.class, args);
    }

}

在SpringBootApplication注解中添加exclude,指定DataSourceAutoConfiguration.class便可。

相关文章
相关标签/搜索