스프링부트에서 sql.init.mode=always를 통해 데이터베이스 초기화할때 주의해야 할 점
스프링부트 3.1.0 기준으로 데이터베이스 초기화를 schema.sql 혹은 data.sql로 할 경우,
sql파일 내에서 -- 블라블라
, <!-- 블라블라 -->
이 아닌 # 블라블라
주석을 사용할때 질의문이 몇개 누락되는 오류 해결 방법을 디버깅을 통해 설명하고 있습니다.
바로 본론으로
레퍼런스 - howto.data-initialization.using-basic-sql-scripts
위 레퍼런스를 읽어보고 대충 요약해보면 다음과 같습니다.
- 애플리케이션이 실행될때 (정확히는 JPA EntityManagerFactory 빈이 생성되기 이전) SQL 스크립트 기반 DataSource 초기화를 할수 있습니다.
- 초기화에 로드시킬 .sql파일은 리소스 폴더에
schema.sql
,data.sql
이런식으로 위치하면 됩니다. - 임베디드 인메모리 데이터베이스를 사용할 때는 기본 적으로
schema.sql
,data.sql
에서 각각의 SQL를 로드합니다. - 그 외 유형의 데이터베이스에서는
spring.sql.init.mode=always
옵션을 사용하여 .sql 초기화 사용여부를 결정할 수 있습니다.
- 초기화에 로드시킬 .sql파일은 리소스 폴더에
저는 MySQL를 사용하고 있었기에 spring.sql.init.mode=always
를 이용해서 데이터베이스를 초기화하고자 했습니다.
1
2
3
4
spring:
sql:
init:
mode: always
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 1. START DELETE TABLE
drop table if exists change_request;
drop table if exists change_request_product;
# 1. END DELETE TABLE
# 2. START CREATE TABLE
create table change_request
(
id bigint auto_increment
primary key,
product_id bigint not null comment '상대물건 fk',
requester_id bigint not null comment '요청자 fk',
request_message varchar(30) not null comment '요청자의 메세지',
reject_message varchar(30) null comment '거절 메세지',
status varchar(15) null comment '요청 상태',
modified_at datetime(6) null,
created_at datetime(6) not null,
removed_by_requestee tinyint(1) default 0 not null,
removed_by_requester tinyint(1) default 0 not null
)
comment '바꿔주세요';
create table change_request_product
(
id bigint auto_increment
primary key,
change_request_id bigint not null comment '교환 요청 fk',
product_id bigint null,
created_at datetime null,
modified_at datetime null
)
comment '바꿔주세요 요청 상세 물건';
# 2. END CREATE TABLE
저는 이렇게 하면, Database connection이 이뤄지면서 schema.sql
안에 모든 내용들이 그대로 commit될 줄 알았습니다.
근데 문제는 SQL 스크립트에서 change_request
테이블이 제대로 생성이 되지 않았습니다…
왜지??? 왜지??? 하면서 검색을 해봤는데… 별다른 포스팅들을 보지 못했습니다.
그래서 스프링부트에서 데이터베이스 초기화 할때 동작하는 코드를 직접 보기로 했습니다.
spring.sql.init
부분의 속성값이 담긴 클래스입니다. 적당한 Getter에 브레이킹 포인터를 걸고, 디버깅해보면…. DataSourceScriptDatabaseInitalizer
의 runScripts
로 도달하게 됩니다.
runScripts
의 매개변수인 scripts
에 스키마 데이터가 잘 들어가 있는것을 확인 할 수 있습니다.
다시 runScripts
메서드로 돌아가면…. ResourceDatabasePopulator
를 생성하여 여기에 모든 내용을 담고, DatabasePopulatorUtils.execute
하는 것을 볼 수 있습니다.
다음은 excute()
입니다.
스텝오버를 해보며 디버깅하다가, populator.populate
를 실행할때 질의문들이 실행되는 것을 체크했습니다.
ResourceDatabasePopulator
의 populate
메서드 구현체를 보면 최종적으로 ScriptUtils.executeSqlScript
를 실행하는 모습을 볼 수 있습니다.
Docs를 보니 저희가 원하는 메서드로 잘 도착한 것 같습니다.
SQL 스크립트도 잘 읽어와졌네요. 그런데 왜 create table이 제대로 실행되지 않았던걸까요?
splitSqlScript()
를 통해 SQL질의를 파싱하고 있네요. 결과를 한번 볼까요?
????????????? CREATE 문 하나가 주석이랑 같이 파싱 되어 있던걸 확인할 수 있었습니다.
그래서 테이블이 제대로 생성되지 않았던 것이었습니다.
Sql을 파싱할때 메서드의 시그니쳐를 보면, 특이한 값이 있습니다.
바로 blockComment...
로 시작하는 변수인데요…
해당 변수로 주석 관련 값을 받아 파싱을 하고 있었습니다.
commentPrefixes
: 한줄 주석 접두사 –blockCommentStartDelimiter
: 블록단위 주석 시작 /*blockCommentEndDelimiter
: 블록단위 주석 끝 */
기존 주석 파싱 기준값이 --
, /* */
로 설정되어 있기 때문에 #
를 통해서 주석을 하는 저에겐 오류가 생겼던 것이지요.
따라서 .sql파일의 주석을 바꿔주었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* 1. START DELETE TABLE ------------- */
drop table if exists change_request;
drop table if exists change_request_product;
/* 1. END DELETE TABLE -------------- */
/* 2. START CREATE TABLE ------------- */
create table change_request
(
id bigint auto_increment
primary key,
product_id bigint not null comment '상대물건 fk',
requester_id bigint not null comment '요청자 fk',
request_message varchar(30) not null comment '요청자의 메세지',
reject_message varchar(30) null comment '거절 메세지',
status varchar(15) null comment '요청 상태',
modified_at datetime(6) null,
created_at datetime(6) not null,
removed_by_requestee tinyint(1) default 0 not null,
removed_by_requester tinyint(1) default 0 not null
)
comment '바꿔주세요';
create table change_request_product
(
id bigint auto_increment
primary key,
change_request_id bigint not null comment '교환 요청 fk',
product_id bigint null,
created_at datetime null,
modified_at datetime null
)
comment '바꿔주세요 요청 상세 물건';
/* 2. END CREATE TABLE ------------ */
결국 잘 파싱되는 것을 볼 수 있었습니다!!
결론…
- 스프링부트에서 sql로 데이터베이스를 초기화할땐 sql 파일 째로 commit되는게 아니라 주석을 제외한 stament들을 각각 파싱을해서 하는거구낭…
- 따라서 주석을 .sql에 포함시키려면 무조건
--
혹은/* */
방식을 써야된다.
- 따라서 주석을 .sql에 포함시키려면 무조건
- 나중에 해당 패키지가 업데이트 된다면…
commentPrefix
등을 yml파일에서 수정할 수 있도록 바꿔주었으면 좋겠다.- 기회된다면 내가 컨트리뷰트 할수도….?
Comments powered by Disqus.