Spring Boot Jpa의 Identity에 대하여

묘한묘랑·2023년 8월 26일
0

ShoppingMall

목록 보기
7/7

프로젝트 진행 도중 마주하게 된 이슈에 대해 알아보다 알게 된 사실이다.

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Getter
  private Long Id;
  
  @Column(nullable = false, unique = true)
  private String userId;

현재 프로젝트에서 Entity의 ID값을 DB가 담당하도록 설계 되어 있다.

그리고 문제의 시작은 Transactional의 Rollback을 이용하여 로직을 설계하던 도중에 발생하게 되었다.

// Repository

boolean regist(Member member){
	memberRepository.save(member);
}

이미 같은 userId가 존재 하였을 때, 무결성을 위반하여 DataIntegrityViolationException이 발생하였기에 Rollback이 되어 ID값도 올라가지 않을거라 판단하였었다.
하지만 테스트를 통하여 확인해보자 예상과는 다르게 나왔다.

예외가 던져졌음에도 불구하고 ID는 여전히 올라갔다.

내가 생각했던 것은 save단 하나로 select문을 쓰지 않고 처리할 수 있을 거라 생각했었지만 그런 일은 일어나지 않았다.

처음에는 CheckedException 때문인가? 라고 생각했었다.

@Transactional(rollbackFor = Exception.class)

그래서 모든 에러를 rollback시키기 위하여 위와 같이 설정해주고 실행 시켜 보았다.

결과는?

보기좋게 실패했다.

그 외에도 saveAndFlush를 걸어본다던지 try/catch로 잡은 후 다른 exception을 던져 본다던지 해보았지만 모두 실패 하였다.

정신줄을 놓기 시작하고 얼마나 지났을까.
갑자기 생각이 들었다.
Identity는 DB가 ID값을 관리 한다던 사실을.

create table member (
member_role smallint not null check (member_role between 0 and 3), 
id bigserial not null, 
nick_name varchar(255), 
pw varchar(255) not null, 
user_id varchar(255) not null unique);

hibernate의 log를 보자 위와 같이 테이블을 만든다는 것을 확인 하였다.
현재 사용하는 DB는 Postgresql이었기에 bigserial을 사용하였는데, postgresql에서 serial은 Sequence를 만들어 Auto_increment 하는 것이었다.
또한 rollback이 되었을 때, sequence는 rollback되지 않으므로 이미 한 번 올라간 값이 rollback되지 않았던 것이었다.

추가적으로 postgresql10 이상에서 serial을 사용 하였을 때, 해당 column을 삭제 시키면 sequence도 같이 삭제된다.

  public boolean regist(Member member) throws AlreadyHasDataException {
    if(memberRepository.findByUserId(member.getUserId()).isPresent())
      throw new AlreadyHasDataException("이미 ID가 존재합니다.");
    memberRepository.save(member);
    return true;
  }

그래서 위와 같이 Id가 존재하였을 때, Custom Exception을 던지는 형태로 사용하기로 바꾸었다.
하지만 select query가 한 번 발생되기 때문에 insert query 한 번으로 처리가 가능한 방법이 있다면 그것을 적용시키고 싶다.

profile
상황에 맞는 기술을 떠올리고 사용할 수 있는 개발자가 되고 싶은 개발자

0개의 댓글