Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Tags
more
Archives
Today
Total
관리 메뉴

히콩쓰 개발 일지

[Consider declaring it as object wrapper for the corresponding primitive type] 발생 원인과 해결 방법 본문

Spring

[Consider declaring it as object wrapper for the corresponding primitive type] 발생 원인과 해결 방법

용히콩 2023. 11. 10. 16:24

게시판 CRUD 기능을 구현할 때 맞닥뜨린 문제이다.

⛔ 전체 오류 메시지
Optional int parameter 'id' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.

int형 parameter id가 primitive type으로 선언되어 null을 표현할 수 없으니 wrapper class 를 사용해보는게 어떻겠냐는 것이다.

아래는 문제가 발생했을 당시 작성한 Board 클래스 이다.

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키 생성 데이터베이스에 위임
    ✅private int id;
    private String title;
    private String author;
    private String password;
    private String content;
    private LocalDateTime createDate;
}

보다시피 private int id로 선언했는데, 이 부분에서 오류가 발생한 것이었다.

  • DB에서 id를 생성하도록 위임했는데 왜 JPA 쪽에서 오류가 날까?
  • DB가 Null을 표현할 수 있는데 primitive typeNull을 표현하지 못해 생기는 오류일까?

결론부터 말하자면, 아니다. ❗JPA의 규약을 어겨 발생한 오류이다❗
(그리고, 이렇게 JPA가 자동으로 관리하도록 하면, DB는 PK의 Null을 허용하지 않지만, JPA는 Null을 가질 수 있도록 강제하는 꼴이다.)


발생 원인 분석

- Primitive Type(원시 타입)과 Wrapper Class(래퍼 클래스)

Primitive Type Wrapper Class
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
  • Primitive TypeWrapper Class를 한 눈에 볼 수 있도록 정리한 표이다.
  • Wrapper ClassPrimitive Type을 객체화 한 것으로, Null 값을 표현할 수 있어 Null로 초기화 되며 Method 사용이 가능하다.
  • 반대로 Primitive TypeNull을 표현할 수 없다. 따라서, 0으로 초기화 된다.

JPA 관점

  • Entity는 자바 객체를 의미한다.
  • 또한 id를 가지고 있다는 것은, DB에서 조회해서 Entity Manager가 관리하고 있다는 것이다.
  • 즉, idJPA에 의해서만 관리되어야 한다.
  • JPA에는 아래와 같은 규약이 존재한다.

idNull이면, Entity에 등록되지 않은 객체로 인식한다.

위 코드에서 id는 Primary Key이다.
⭐JPA 관점에서는 Primitive Type으로 선언된 idNull이 없이 모두 숫자이기 때문에,
관리되고 있는 Entity인ㅇ지 새로 만든 Entity 인지 구분할 수 없다.
id가 0이라고 해도, 등록되지 않은 Entity인지 PK가 0인 것인지 구분할 수 없다는 것이다.


해결 방법

위와 같은 이유로, Null 값을 허용하는 Wrapper Class로 바꾸기로 했다.
여기서 한 가지 고민이 더 생겼다.

intWrapper ClassInteger를 사용하는 것이 좋을까?
사실, 현업에서는 중간에 데이터가 추가될 경우를 고려해 id 를 1씩 증가시키는 것이 아닌, 10씩 증가시키는 경우도 많다고 한다.
이런 경우, Integer 의 표현 가능한 값의 범위가 21억 이라면 2.1억으로 줄어드는 것이다.
⭐ 하지만 많은 양의 데이터를 담아야 하므로, Integer 보다는 long의 Wrapper ClassLong 의 사용을 권장한다고 한다.

나는 조언을 받아들여, Long 을 사용하기로 했다❗

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키 생성 데이터베이스에 위임
    ✅private Long id;
    private String title;
    private String author;
    private String password;
    private String content;
    private LocalDateTime createDate;

    public Board(String title, String author, String password, String content, LocalDateTime createTime) {
        this.title = title;
        this.author = author;
        this.password = password;
        this.content = content;
        this.createDate = createTime;
    }
}

private Long id 로 수정한 결과이다.
id (= PK) 의 생성을 DB에 위임했다는 점을 강조하기 위해, 위에서 작성한 코드와 다르게 Board Class의 생성자를 추가해 작성해보았다.


➕ 추가로,
Board 객체가 생성될 때 id는 DB가 알아서 넣어줄 것인데, createDate 즉 작성일까지 DB에 의존하는 것은 좋지 않을 것이라고 판단해

LocalDateTime.now() 를 사용하여 현재 시간을 넣어 DB에 전달하도록 구현하였다.

오늘도 해결 끝 💕