포스트

스프링부트에서 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 초기화 사용여부를 결정할 수 있습니다.

저는 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에 브레이킹 포인터를 걸고, 디버깅해보면…. DataSourceScriptDatabaseInitalizerrunScripts로 도달하게 됩니다.

runScripts의 매개변수인 scripts에 스키마 데이터가 잘 들어가 있는것을 확인 할 수 있습니다.

다시 runScripts 메서드로 돌아가면…. ResourceDatabasePopulator를 생성하여 여기에 모든 내용을 담고, DatabasePopulatorUtils.execute하는 것을 볼 수 있습니다.

다음은 excute() 입니다.

스텝오버를 해보며 디버깅하다가, populator.populate를 실행할때 질의문들이 실행되는 것을 체크했습니다.

ResourceDatabasePopulatorpopulate 메서드 구현체를 보면 최종적으로 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에 포함시키려면 무조건 -- 혹은 /* */방식을 써야된다.
  • 나중에 해당 패키지가 업데이트 된다면… commentPrefix등을 yml파일에서 수정할 수 있도록 바꿔주었으면 좋겠다.
    • 기회된다면 내가 컨트리뷰트 할수도….?
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

Comments powered by Disqus.