상황은 이렇다.
Item.java
package com.example.shop;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;
@Entity
@Data
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
private String title;
private Integer price;
}
Item 클래스(엔티티)에서 @Data 어노테이션은 Lombok 라이브러리, @Id 어노테이션은 JPA 라이브러리이다
먼저 오류에 대해 확인을 해보면 기본키(id) 값이 이미 있는데 생성을 하려고 해서 기본키 위반에 해당한다고 오류가 발생했다.
그래서
Item 클래스(엔티티)에서 어노테이션 변경을 해봤다.
Item.java
package com.example.shop;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Entity
@Getter
@Setter
@ToString
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
private String title;
private Integer price;
}
@Getter @Setter 어노테이션을 이용해서 데이터를 삽입했을 때는 이상 없이 됐다.
그럼 왜 @Data 어노테이션을 사용하면 Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.ITEM(ID)
오류가 발생하는지 검색을 해봤는데 잘 나오지 않는 것 같아 ChatGPT에게 물어봤다.
@Data 어노테이션을 사용하면 자동으로 equals와 hashcode 메서드를 생성하는데 id 필드가 있어 같이 id 필드가 포함된 상태에서 hashcode가 생성된다.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)는 데이터베이스에서 기본키 값을 자동생성하는 어노테이션이다. 즉 JPA가 엔티티를 데이터베이스에서 저장할 기본키 값이 자동으로 생성된다.
여기서 이미 1번 기본키 값이 있는 상태에서 새로운 item 객체를 생성하면 동일한 hashcode값을 가진 item객체가 생성될 수 있다.
그러면 데이터베이스에서는 새로운 객체를 저장하려고 할 때 동일한 id 값을 가진 데이터로 인식되어 해당 오류가 발생하게 된다.
라는 답변이 왔다.
해당 오류에 관해 검색해보다가 어떤 블로그(참고 블로그)를 봤는데 @Data 어노테이션을 Entity에서는 사용하지 않는다고 정리해둔 글이 있는데 데이터베이스와 관련이 있기 때문에 사용을 할 경우 위험할 수 있다는 부분이 아마 이 부분이지 않을까 싶기도 한다.
좀 더 쉽게 얘기하면 @Data 어노테이션에서 데이터베이스와 같은 id값을 생성할 수도 있고 그러면 기존에 데이터베이스에 저장되어 있던 id값이 있는 상태에서 저장하려고 해서 기본키 위반에 해당하는 상황이 나올 수 있다 라고 정리가 되지 않을까 싶다.
엔티티에서 안 쓰는 것이 좋다고 하지만 어떤 해결방법이 있는지 알아보자.
일단 id값을 equals와 hashcode 계산에서 제외를 하면 된다. 그럴려면 위에서 @Data 어노테이션 대신
@Data
@EqualsAndHashCode(exclude = "id")
@Entity
public class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private Integer price;
}
참고