본문 바로가기

Backend/Spring | SpringBoot

[JPA] Auto Increase가 안될 때

반응형

역시 기계는 솔직하다

삽질(이전글1, 이전글2)은 내가 해놓고 원인을 못찾고있었으니...

 

데이터를 먼저 올리고 Auto Increase가 안될 때 여러가지 경우가 있다.(경험치....)

  1. @GeneratedValue 전략이 잘못 되었을 때
    GeneratedValue에서 기준이 잘못되어 ID값이 잘못 부여되는 경우가 많다고 한다. (관련 글)
  2. properties 혹은 yml파일에서의 설정
    - Spring에서는 Script-base 방식과 Hibernate의 생성 방식과 같이 다양한 데이터 초기화 기술을 함께 쓰는 것을 권장하지 않음(참고 글)
    - init.schema 후 init.data / 세트로 둘이 같이 있어야 한다.
    # application.properties
    spring.jpa.database=h2
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
    spring.jpa.hibernate.ddl-auto=create
    spring.jpa.generate-ddl=true
    spring.jpa.show-sql=true
    
    spring.sql.init.schema-locations=classpath*:/static/db/h2.sql
    spring.sql.init.data-locations=classpath*:/static/db/insert.sql
    
    
    # application.yml
    spring:
    	jpa:
    		database: h2
    		database-platform: org.hibernate.dialect.H2Dialect
    		hibernate
    			ddl-auto: create
    		generate-ddl: true
    		show-sql: true
    	sql:
    		init:
    			schema-locations: classpath*:/static/db/h2.sql
    			data-locations: classpath*:/static/db/insert.sql​
  3. 데이터를 먼저 추가할 때의 DDL, DML 작성
    테이블 관계를 생각해서 FK가 있다면 바라보는(참조되는) 테이블 및 데이터를 먼저 정의
    <!-- DDL -->
    CREATE TABLE member(
                           member_id bigint primary key NOT NULL auto_increment,
                           nick_name varchar(10) NOT NULL,
                           email varchar(50) NOT NULL,
                           password varchar(20) NOT NULL,
                           acc_time varchar(10),
                           member_status varchar(10) NOT NULL
    );
    
    CREATE TABLE board(
                          board_seq bigint primary key NOT NULL auto_increment,
                          title varchar(50) NOT NULL,
                          content varchar(200) NOT NULL,
                          board_ctgr varchar(10) NOT NULL,
                          board_status varchar(10) NOT NULL,
                          member_id bigint NOT NULL
    );
    
    <!-- DML : @ID를 입력하지 않아도 괜찮다. -->
    -- MEMBER TABLE
    INSERT INTO member ( nick_name, email, password, member_status ) VALUES ( 'JOY', 'test@test.com', 'test1234', 'Active' );
    
    -- BOARD TABLE
    INSERT INTO board ( title, content, board_ctgr, board_status, member_id ) VALUES( 'test1', 'test_text1', 'noti', 'Active', 1 );​

 

 

한줄 요약하면 

Entity의 @GeneratedValue 전략 확인, properties 혹은 yml에서 jpa와 sql 초기화 부분확인, DDL, DML 추가 순서 확인

 

 

+ ) auto_increase는 해결 되었으나 기존에 밀어 넣으려던 데이터가 삽입되지 않았다.

LogLevel을 한단계 상위에 Trace를 걸어주었다.

logging.level.root=DEBUG
logging.level.org.springframework=TRACE
logging.level.org.springframework.jdbc=DEBUG
logging.level.org.hibernate=TRACE
logging.level.org.hibernate.SQL=DEBUG

 

로그를 확인중에 깨달은 사실

[ datasource.init ] : Create - [ datasource.init ] : Insert - [ hibernate ] : Drop - [ hibernate ] : Create

이렇게만 진행된다.

그러니 데이터가 없을 수 밖에....

 

++ ) 그리고 db를 Embeded로 했는지 In-Memory로 했는지도 실행의 분기점이 되었던것 같다.

내가 해본 케이스들을 정리해 보았다. 

Embeded / In-Memory ddl-auto init insert_seq필요 data유무 auto_increase
Embeded none defer-datasource-initialization=true dont' need 없음 SUCCESS
init.mode=always need hibernate 중복 -
create | update defer-datasource-initialization=true dont' need 없음 SUCCESS
init.mode=always need hibernate 중복 -
create-drop defer-datasource-initialization=true dont' need 없음 SUCCESS
init.mode=always need hibernate 중복 -
In-Memory none defer-datasource-initialization=true need 없음 Fail
init.mode=always dont' need 있음 SUCCESS
create

defer-datasource-initialization=true need 있음 Fail
init.mode=always dont' need 없음 SUCCESS
update defer-datasource-initialization=true need 있음 Fail
init.mode=always dont' need 있음 SUCCESS
create-drop defer-datasource-initialization=true need 있음 Fail
init.mode=always dont' need 없음 SUCCESS

 

+++ ) 초기화를 같이 하면 안된다고 생각하고 있었다.

하지만 찾아보다보니 어디는 같이 사용하면 안된다고 하고.. 어디는 같이 사용해도 괜찮다고 하고...
결과적으로는 같이 사용해도 괜찮았다...

logging.level.root=DEBUG
logging.level.org.springframework=TRACE
logging.level.org.springframework.jdbc=DEBUG
logging.level.org.hibernate=TRACE
logging.level.org.hibernate.SQL=DEBUG

spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:mem:testdb
#spring.datasource.url=jdbc:h2:~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.database=h2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.defer-datasource-initialization=true

spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath*:/static/db/h2.sql
spring.sql.init.data-locations=classpath*:/static/db/insert.sql

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

 

Spring Boot에서는 `spring.jpa.hibernate.ddl-auto` 설정과 `spring.sql.init` 설정을 통해 데이터베이스 초기화 순서를 제어할 수 있습니다.
`spring.jpa.hibernate.ddl-auto` 속성이 `create` 또는 `create-drop`으로 설정되어 있으면 Hibernate가 데이터베이스 스키마를 생성합니다.
`spring.sql.init.mode` 속성은 SQL 스크립트를 통한 초기화를 제어합니다.
`spring.jpa.hibernate.ddl-auto`가 `create`로 설정되어 있고, `spring.sql.init.mode`가 `always`로 설정되어 있다면, 일반적으로 Hibernate가 먼저 실행되어 스키마를 생성하고, 그 다음에 `spring.sql.init` 설정에 따라 SQL 스크립트가 실행됩니다.
문제는 `spring.jpa.defer-datasource-initialization` 설정 때문에 발생할 수 있습니다.
이 설정이 `true`일 경우, 데이터 소스 초기화가 ApplicationContext가 완전히 로드된 후까지 지연됩니다.
이는 `spring.sql.init` 스크립트가 Hibernate DDL 실행 후에 실행되도록 하는데 도움이 될 수 있지만, 주석 처리되어 있어 현재 활성화되어 있지 않습니다.
현재 설정에서 Hibernate가 `spring.sql.init`보다 먼저 실행되어야 하는 것이 목표라면, 다음과 같이 확인해야 합니다:
1. `spring.jpa.hibernate.ddl-auto`가 `create` 또는 `create-drop`으로 설정되어 있는지 확인: 이 설정이 데이터베이스 스키마 생성을 제어합니다.
2. `spring.jpa.defer-datasource-initialization`를 `true`로 설정: 이는 `spring.sql.init` 스크립트 실행을 지연시켜 Hibernate가 스키마를 먼저 생성할 수 있도록 합니다.
3. `spring.sql.init.mode` 설정 확인: 이 설정은 SQL 스크립트가 언제 실행될지를 제어합니다. `always`로 설정되어 있다면, ApplicationContext 로딩이 완료된 후에 SQL 스크립트가 실행됩니다.
위의 설정을 통해 Hibernate가 먼저 데이터베이스 스키마를 생성하고, 그 다음에 `spring.sql.init`에 의해 SQL 스크립트가 실행되도록 할 수 있습니다.
설정이 이미 이와 같이 구성되어 있다면, Hibernate가 먼저 스키마를 생성하고 이후에 SQL 초기화 스크립트가 실행되어야 합니다.
반응형