히콩쓰 개발 일지
[Consider declaring it as object wrapper for the corresponding primitive type] 발생 원인과 해결 방법 본문
[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 type
은Null
을 표현하지 못해 생기는 오류일까?
결론부터 말하자면, 아니다. ❗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 Type
과Wrapper Class
를 한 눈에 볼 수 있도록 정리한 표이다.Wrapper Class
는Primitive Type
을 객체화 한 것으로,Null
값을 표현할 수 있어 Null로 초기화 되며 Method 사용이 가능하다.- 반대로
Primitive Type
은Null
을 표현할 수 없다. 따라서, 0으로 초기화 된다.
JPA 관점
- Entity는 자바 객체를 의미한다.
- 또한
id
를 가지고 있다는 것은, DB에서 조회해서Entity Manager
가 관리하고 있다는 것이다. - 즉,
id
는 JPA에 의해서만 관리되어야 한다. - JPA에는 아래와 같은 규약이 존재한다.
id
가 Null
이면, Entity에 등록되지 않은 객체로 인식한다.
위 코드에서 id
는 Primary Key이다.
⭐JPA 관점에서는 Primitive Type
으로 선언된 id
는 Null
이 없이 모두 숫자이기 때문에,
관리되고 있는 Entity인ㅇ지 새로 만든 Entity 인지 구분할 수 없다.id
가 0이라고 해도, 등록되지 않은 Entity인지 PK가 0인 것인지 구분할 수 없다는 것이다.
해결 방법
위와 같은 이유로, Null
값을 허용하는 Wrapper Class
로 바꾸기로 했다.
여기서 한 가지 고민이 더 생겼다.
int
의Wrapper Class
인Integer
를 사용하는 것이 좋을까?
사실, 현업에서는 중간에 데이터가 추가될 경우를 고려해id
를 1씩 증가시키는 것이 아닌, 10씩 증가시키는 경우도 많다고 한다.
이런 경우,Integer
의 표현 가능한 값의 범위가 21억 이라면 2.1억으로 줄어드는 것이다.
⭐ 하지만 많은 양의 데이터를 담아야 하므로,Integer
보다는 long의Wrapper Class
인Long
의 사용을 권장한다고 한다.
나는 조언을 받아들여, 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에 전달하도록 구현하였다.
오늘도 해결 끝 💕
'Spring' 카테고리의 다른 글
[Spring Security] 권한을 올바르게 설정했는데 403 에러가 날 경우 고려해야 할 점 (2) | 2023.12.12 |
---|---|
Controller Test 403 forbidden 해결 (1) | 2023.12.05 |
Primitive Type(원시타입)과 Wrapper Class(래퍼 클래스) 의 Getter (1) | 2023.11.22 |
Spring Boot 프로젝트 Dto 사용 시 HTTP 406 Error 해결 방법! (0) | 2023.11.21 |
@ReqestBody String 사용 시 Json이 그대로 입력되는 이유 (1) | 2023.11.14 |