Spring Boot can auto-configure embedded H2, HSQL, and Derby databases. You need not provide any connection URLs. You need only include a build dependency to the embedded database that you want to use. If there are multiple embedded databases on the classpath, set the
spring.datasource.embedded-database-connection
configuration property to control which one is used. Setting the property tonone
disables auto-configuration of an embedded database.스프링 부트는 내장된 H2, HSQL, Derby 데이터베이스를 자동으로 구성할 수 있어 연결 URL을 제공할 필요가 없다. 단지 사용하고자 하는 내장 데이터베이스의 의존성을 추가해주기만 하면 된다. 만약 클래스경로에 여러개의 내장 데이터베이스가 있다면,
spring.datasource.embadded-database-connection
property를 설정하여 사용할 데이터베이스를 제어할 수 있다. 해당 설정 값을none
으로 지정하면 내장 데이터베이스 자동 설정을 비활성화 할 수 있다.
스프링 부트는 내장 데이터베이스가 있어 해당 의존성을 build.gradle
과 같은 의존성 관리에 추가하는 것으로 자동으로 데이터베이스를 구성할 수 있다. 물론 이 자동 구성은 설정 값을 변경해주는 것으로 비활성화도 가능하다. 그렇다면 내장 데이터베이스가 아닌 다른 데이터베이스를 사용하고 싶다면 어떻게 하면 될까?
You should at least specify the URL by setting the
spring.datasource.url
property. Otherwise, Spring Boot tries to auto-configure an embedded database.최소한
spring.datasource.url
property에 특정한 URL을 지정해 주어야 한다. 그렇지 않으면 스프링 부트는 자동으로 내장 데이터베이스를 구성하려고 시도한다.
정리하자면 URL이 명시되어 있으면 해당 URL로 커넥션을 연결하고, URL이 명시되어 있지 않다면 자동으로 내장 데이터베이스를 생성한다.
그런데 URL을 명시하지 않아서 자동으로 내장 데이터베이스를 구성할 때 URL은 어떻게 되는 걸까? 따로 지정되는 특정한 URL이 있을까?
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@AutoConfigureBefore(SqlInitializationAutoConfiguration.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class,
DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
...
}
DataSource를 설정해주는 DataSourceAutoConfiguration
빈을 보면, DataSource.class
및 XADataSource.class
빈을 찾지 못할 경우, EmbeddedDataSourceConfiguration
를 바탕으로 빈을 등록한다.
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(DataSourceProperties.class)
public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware {
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Bean(destroyMethod = "shutdown")
public EmbeddedDatabase dataSource(DataSourceProperties properties) {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseConnection.get(this.classLoader).getType())
.setName(properties.determineDatabaseName()).build();
}
}
EmbeddedDataSourceConfiguration
클래스 안에는 EmbeddedDatabase를 빈으로 등록하는 dataSource 메서드가 있는데, 주입된 DataSourceProperties
의 determineDatabaseName 메서드를 통해 데이터베이스의 이름을 결정한다.
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
...
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";
}
return null;
}
...
}
DataSourceProperties
는 property 파일의 spring.datasource
부분을 읽어오게 되는데, application.property에 데이터베이스 URL과 이름을 지정하지 않았기 때문에 uniqueName은 null 값이 된다. 따라서 uniqueName은 자동으로 UUID가 제공하는 무작위 이름으로 설정된다.
때문에 EmbeddedDataBaseBuilder
안의 databaseFactory
필드 (여기서는 EmbeddedDatabaseFactory
)에 무작위 이름이 저장된다. 그리고 build 메서드를 통해 EmbeddedDatabaseFactory
가 DataSource를 만들어서 빈으로 등록하게 된다.
결과적으로, URL 및 이름을 명시하지 않을 경우 매 번 스프링 부트가 올라갈 때 마다 새로운 이름의 데이터베이스가 생성된다. 따라서 만약 여러 개의 SpringBootTest를 한번에 돌리는 경우가 있을 때, 테스트들끼리 같은 데이터베이스를 공유하는 문제가 생기지 않는다.
Spring Boot can automatically create the schema (DDL scripts) of your DataSource and initialize it (DML scripts). It loads SQL from the standard root classpath locations: schema.sql and data.sql, respectively. In addition, Spring Boot processes the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.datasource.platform. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).
스프링 부트는 자동으로 DataSource의 스키마(DDL 스크립트)를 생성하고 초기화(DML 스크립트)할 수 있다. 표준 루트 클래스 경로 위치(각각 schema.sql 및 data.sql)에서 SQL을 로드한다. 또한 Spring Boot는 schema-${platform}sql 및 data-${platform}sql 파일(파일이 있는 경우에)을 처리한다. 여기서 platform은 spring.datasource.platform의 값이다. 필요한 경우 데이터베이스별 스크립트로 전환할 수 있다. 예를 들어 데이터베이스의 벤더 이름(hsqldb, h2, oracle, mysql, postgresql 등)으로 설정할 수 있다.
Spring Boot automatically creates the schema of an embedded DataSource. This behavior can be customized by using the spring.datasource.initialization-mode property. For instance, if you want to always initialize the DataSource regardless of its type:
spring.datasource.initialization-mode=always
스프링 부트는 자동으로 내장 DataSource의 스키마를 생성한다. 이는 property의
spring.datasource.initialization-mode
속성을 사용하여 지정할 수 있다. 예를 들어, 매번 타입에 상관 없이 DataSource를 초기화 하기를 원한다면 다음의 옵션을 사용한다.
spring.datasource.initialization-mode=always
때문에 schema.sql
파일에 CREATE TABLE
명령이 있을 경우 테이블이 이미 데이터베이스 상에 존재하고 있다면 schema.sql
파일의 상단에 DROP TABLE
을 해줘야 이미 존재하고 있는 테이블을 다시 생성하려고 하는 오류를 방지할 수 있다.
@AutoConfigureTestDatabase
Annotation that can be applied to a test class to configure a test database to use instead of the application-defined or auto-configured DataSource. In the case of multiple DataSource beans, only the @Primary DataSource is considered.
어플리케이션이 정의하거나 또는 자동 구성한 DataSource 대신 사용할 테스트 데이터베이스를 구성하기 위해 테스트 클래스에 적용할 수 있는 어노테이션이다. 여러 DataSource가 존재할 경우 @Primary DataSource만 고려된다.
테스트에 @AutoConfigureTestDatabase
어노테이션을 붙여주면 property로 설정한 데이터베이스가 아닌 내장 데이터베이스를 사용하도록 강제할 수 있다. 다만 이 어노테이션을 통해 기본 내장 데이터베이스를 사용한다면 application.properties
에 설정한 MODE
따위의 H2 설정이 적용되지 않는다는 것을 명심하자.
추가적으로, 테스트 관련 어노테이션 중 @DataJdbcTest
및 @DataJpaTest
는 @AutoConfigureTestDatabase
를 포함하기 때문에 기본 내장 데이터베이스를 사용한다.
참고 자료
Spring Boot Reference 85.Data Access
Spring Boot Reference 86.Database Initialization
@AutoConfigureTestDatabase우테코 최고 미남 영앤리치 제이슨(박재성)
(필즈) ㄷㄷ.. 와 잘봤습니다.. 대박이네요