개발 이론/Spring

[Spring] Spring Boot와 데이터베이스

dal_been 2023. 11. 7. 23:33
728x90

 

스프링 부트는 자동으로 내장 데이터베이스를 생성한다

 

build.gradle에 스프링 부트에 내장된 H2, HSQL,Derby 데이터베이스 의존성을 추가하면 자동으로 데이터베이스를 구성할 수 있다.

 

근데 만약 클래스 경로에 여러 내장 데이터베이스가 존재한다면 spring.datasource.embadded-database-connection 을 이용하여 어떤 데이터베이스를 사용할 것인지 선택할 수 있다. 만약 none으로 한다면 내장 데이터베이스 자동설정은 비활성화 된다

 

또한 spring.datasource.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 {

	}
    ...
}

 

 

여기서  이부분을 보면 해당 빈들을 못찾는 경우 EmbeddedDataSourceConfiguration을 바탕으로 빈을 등록한다

@ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import(EmbeddedDataSourceConfiguration.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();
	}

}


dataSource의 메서드를 보면 DataSourceProperties가 주입되는데 이를 통해 데이터베이스의 이름이 결정된다

 

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

 

코드를 보면 null 이면 UUID를 통해 무작위로 이름을 설정한다

 

 

그래서 결과적으로는 스프링부트가 올라갈때마다 새로운 이름의 데이터베이스가 생성된다. 만약 여러개의 SpringBootTest를 한번에 올리더라도 데이터베이스를 공유하는 문제는 생기지 않는다

 

 

 

스프링부트 sql 파일 자동으로 읽어오다

 

스프링 부트는 schema.sql 이나 data.sql에서 SQL을 로드한다

 

다만 주의할점은 schema.sql파일에 create table 명령만 있을 경우 테이블이 이미 데이터베이스에 존재한다면 오류가 발생할 것이다

그래서 drop table을 파일 상단에 해주는 것이 오류를 방지할 수 있다

 

 

schema.sql
- 주로 DDL 작성
- DDL : 데이터베이스 계정, 스키마, 테이블, 인데스, 제약사항등 데이터베이스 구조를 정의할때 사용

data.sql
- 주로 DML 작성
- DML : insert,update, delete 등 데이터 조작하는데 사용

 

 

또한 내장형 인메모리 데이터 베이스가 아니고 다른 데이터 베이스를 사용하는 상황에서는 스크립트를 이용해 데이터베이스를 초기화하려면

 

application.properteis 파일

spring.sql.init.mode = always, embedded, never

 

를 설정해야한다

 

  • embedded : 인메모리 가 사용될때만 데이터 베이스 초기화 실행
  • always : 인메모리 아닌 다른 실제 데이터베이스 사용시 데이터 베이스 초기화 하려면 always 사용

 

 

테스트시 @AutoConfigureTestDatabase

 

테스트에 위의 어노테이션을 붙여주면 property로 설정한 데이터베이스가 아닌 내장 데이터 베이스를 사용하도록 강제할 수있다

다만 이 어노테이션을 사용한다면 application.properties에 설정한 mode 설정은 적용되지 않는다

 

@DataJdbcTest나 @DataJpaTest는 @AutoConfigureTestDatabase를 포함하기 때문에 기본으로 내장 데이터베이스르 사용한다