JdbcSQLIntegrityConstraintViolationException - @GeneratedValue의 GenerationType

2023. 1. 6. 10:15프로젝트/[Sleeper] 수면관리 어플리케이션

728x90

 

문제 발생

 

 

회원가입과 관련된 코드를 리팩토링한 후에 postman으로 테스트를 한 순간...아래와 같은 에러가 발생했다.

 

org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.USER(USER_PK) [1, 'ADMIN', 24, 'sleeper', STRINGDECODE('\uad00\ub9ac\uc790\ub2e4'), STRINGDECODE('\uad00\ub9ac\uc790'), 'sleeper123@', TIME '23:30:00', TIME '07:30:00', 1, 1]"; SQL statement:

 

에러 내용을 읽어보면 기본 키 규칙이 위배되었다는 뜻이다. 에러 내용을 읽자마자 무언가 짚이는 게 있어 코드를 확인해 봤더니 역시나... @GeneratedValue와 관련된 오류였다.

 

현재 상황

 

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "USER")
public class User {

   @Id
   @GeneratedValue
   private Long userPk;

 

현재 @GeneratedValue를 사용하여 PK 값을 부여하고 있습니다.

INSERT INTO USER (USER_ROLE_TYPE, USER_AGE, USER_ID, USER_MESSAGE, USER_NICK_NAME, USER_PASSWORD, user_goal_wake_time, user_goal_sleep_time, money_fk, character_fk) VALUES ('ADMIN',24,'sleeper','관리자다','관리자','sleeper123@','07:30','23:30',1,1);

 

현재 제 프로젝트에서는 data.sql에 관리자 데이터가 삽입되어있는 상태입니다.

그리고, GeneratedValue의 기본 값인 AUTO를 사용중입니다.

 

 

문제 분석 및 의문

 

에러 메세지와 현재 상황을 종합해보면,  관리자 데이터의 PK값은 1이고, 회원가입하면서 만들어질 회원 데이터의 PK값도 1이라서 발생하는 문제이다. 그렇다면, 왜 회원데이터의 PK값이 2가 되지않고 1부터 생성되는 것일까??

 

이를 위해서는 기본키 생성 전략에 대한 공부가 선행되어야한다.

 

 

@GeneratedValue의 GenerationType

 

  • IDENTITY : 기본키 생성을 데이터베이스에게 위임하는 방식으로 ID값을 따로 할당하지 않아도 데이터베이스가 자동으로 AUTO_INCREMENT를 하여 기본키를 생성해준다. 이 방식은 데이터베이스에 값을 저장한 후에야 기본 키 값을 구할 수 있다.
EntityManager.persist() -> 트랜잭션 커밋 -> flush -> 데이터베이스 저장 -> 식별자 조회 ->
조회한 식별자를 엔티티의 식별자에 할당

Hibernate는 JDBC3에 추가된 Statement.getGenerateKeys()를 사용해 DB와 한번만 통신한다. 해당 메소드는 DATA를 저장하면서, 생성된 기본 키 값을 가져온다.

 

  • SEQUENCE : 데이터 베이스의 Sequence Objcet를 사용하여 데이터베이스가 자동으로 기본키를 생성해준다.            @SequenceGenerator 가 필요하다.
EntityManager.persist() -> 데이터베이스 시퀀스를 사용해 식별자 조회 ->조회한 식별자를 엔티티에 할당 ->
엔티티를 영속성 컨텍스트에 저장 -> 트랜잭션 커밋 -> flush -> 데이터베이스 저장

 

  • TABLE : 모든 데이터베이스에서 사용 가능하다(하지만, 최적화 관련 이슈가 있다.)

키 생성 전용 테이블을 생성하여, 데이터베이스 시퀀스처럼 사용한다.

 

 

  • AUTO : 기본 설정 값으로 각 데이터베이스에 따라 기본키를 자동으로 생성한다.

AUTO는 Generation type의 defualt값이다. 데이터 베이스의 종류에따라서 위 3가지 전략 중 1개를 JPA에서 자동으로 선택해준다.

 

 

제 코드에서는 Generation type을 auto로 설정해놓았습니다.

 

그리고, H2 DB에서 Sequence 전략을 지원하기 때문에, Sequence 전략으로 실행되었으리라 예상합니다.

일단, 제 코드의 문제는 Sequence 전략을 사용시에 @SequenceGenerator를 사용해야하는데 사용하지 않았습니다.

 

만약, 현재 제 상황에서  Sequence전략을  @SequenceGenerator를 붙여줘야하고, 이 애노테이션의 속성인 initialValue(DDL)은 기본 값이 1이기 때문에 이를 다른 값으로 바꿔주어야합니다.

 

 

문제 해결

 

@GeneratedValue의 전략을 IDENTITY라고 명시하면 위와 같은 에러가 해결된다.

왜냐하면 이 전략은 데이터베이스에 엔티티를 저장한 후에 식별자를 엔티티의 식별자 값에 할당하는 방식이기 때문에 H2 데이터베이스에 미리 관리자 데이터를 넣어둔다해도, 식별자 값이 겹칠 수가 없다!

 

 

JPA에대한 공부를 프로젝트를 진행하면서 같이 병행하고 있어서 위와같은 오류가 나는 것 같다.

하지만, 이론만 공부하는 것보다 직접 에러를 마주한 후 에러를 해결하기 위해 관련 이론을 공부하니 받아들이기 쉬운 것 같다. 

 

이상입니다!!!

 

 

728x90