網頁

2020/7/28

Spring Boot H2資料庫datasource預設配置

Spring Boot對 embbeded的H2資料庫的datasource自動配置預設如下。

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

以上預設值是由Spring Boot的
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
org.springframework.boot.jdbc.EmbeddedDatabaseConnection而來。

系統啟動時會透過DataSourceAutoConfiguration進行自動配置,讀取DataSourceProperties的配置資訊。

在Spring Boot的EmbeddedDatabaseConnection中設定有H2資料庫預設的連線資訊,節錄原始碼(2.2.x)如下:

EmbeddedDatabaseConnection

public enum EmbeddedDatabaseConnection {

    /**
     * No Connection.
     */
    NONE(null, null, null),

    /**
     * H2 Database Connection.
     */
    H2(EmbeddedDatabaseType.H2, "org.h2.Driver", "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"),

    /**
     * Derby Database Connection.
     */
    DERBY(EmbeddedDatabaseType.DERBY, "org.apache.derby.jdbc.EmbeddedDriver", "jdbc:derby:memory:%s;create=true"),

    /**
     * HSQL Database Connection.
     */
    HSQL(EmbeddedDatabaseType.HSQL, "org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:%s");
    ...

    /**
     * Returns the URL for the connection using the specified {@code databaseName}.
     * @param databaseName the name of the database
     * @return the connection URL
     */
    public String getUrl(String databaseName) {
        Assert.hasText(databaseName, "DatabaseName must not be empty");
        return (this.url != null) ? String.format(this.url, databaseName) : null;
    }

}

jdbc:h2:mem:%s後面的%s會透過格式化字串為資料庫名稱,預設為testdb

注意Spring Boot 2.3.0開始spring.datasource.generate-unique-name預設為true,因此H2資料庫名稱是亂數產生,不再為預設的testdb,所以設定spring.datasource.url=jdbc:h2:mem:testdb指定資料庫名稱為testdb

不設定資料庫名稱的話可在啟動時的log找到產生的亂術資料庫名稱,例如下面的ec6b9859-df9f-48ca-8dd4-2a4eb3b21bab

2020-12-31 14:25:04.360  INFO 10640 --- [           main] c.z.h.HikariDataSource                   : HikariPool-1 - Starting...
2020-12-31 14:25:04.588  INFO 10640 --- [           main] c.z.h.HikariDataSource                   : HikariPool-1 - Start completed.
2020-12-31 14:25:04.595  INFO 10640 --- [           main] o.s.b.a.h.H2ConsoleAutoConfiguration     : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:ec6b9859-df9f-48ca-8dd4-2a4eb3b21bab'

DataSourceProperties

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    ...
    /**
     * Name of the datasource. Default to "testdb" when using an embedded database.
     */
    private String name;
    ...
    /**
     * Determine the driver to use based on this configuration and the environment.
     * @return the driver to use
     * @since 1.4.0
     */
    public String determineDriverClassName() {
        if (StringUtils.hasText(this.driverClassName)) {
            Assert.state(driverClassIsLoadable(), () -> "Cannot load driver class: " + this.driverClassName);
            return this.driverClassName;
        }
        String driverClassName = null;
        if (StringUtils.hasText(this.url)) {
            driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
        }
        if (!StringUtils.hasText(driverClassName)) {
            driverClassName = this.embeddedDatabaseConnection.getDriverClassName(); // 取得Driver Class名稱
        }
        if (!StringUtils.hasText(driverClassName)) {
            throw new DataSourceBeanCreationException("Failed to determine a suitable driver class", this,
                    this.embeddedDatabaseConnection);
        }
        return driverClassName;
    }
    ...
    /**
     * Determine the name to used based on this configuration.
     * @return the database name to use or {@code null}
     * @since 2.0.0
     */
    public String determineDatabaseName() {
        if (this.generateUniqueName) {
            if (this.uniqueName == null) {
                this.uniqueName = UUID.randomUUID().toString();
            }
            return this.uniqueName;
        }
        if (StringUtils.hasLength(this.name)) {
            return this.name;
        }
        if (this.embeddedDatabaseConnection != EmbeddedDatabaseConnection.NONE) {
            return "testdb"; // 預設embedded資料庫名稱
        }
        return null;
    }
    ...
    /**
     * Determine the username to use based on this configuration and the environment.
     * @return the username to use
     * @since 1.4.0
     */
    public String determineUsername() {
        if (StringUtils.hasText(this.username)) {
            return this.username;
        }
        if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
            return "sa"; // 預設使用者名稱
        }
        return null;
    }
    ...
    /**
     * Determine the password to use based on this configuration and the environment.
     * @return the password to use
     * @since 1.4.0
     */
    public String determinePassword() {
        if (StringUtils.hasText(this.password)) {
            return this.password;
        }
        if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
            return ""; // 預設密碼
        }
        return null;
    }
    ...
}

沒有留言:

張貼留言