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便可。

相關文章
相關標籤/搜索