Spring JPA에서 Entity 생성시, 특정 컬럼이 null이 될수 없다는 에러에 대해 알아보도록 하겠습니다.
JPA를 사용하여 개발하던 도중, Embedded Value 타입의 값을 적지 않는 경우, cannot be null 에러가 발생되는 현상이 나타났습니다. 그동안 개발하는 도중에는 한번도 마주한적이 없었는데, 특이한 경우여서 정리를 해볼까합니다.
JPA Column '' cannot be null
1. 에러 발생 시나리오
Entity를 저장하는 로직을 개발중에 있었습니다. 이때 Entity안의 특정 Embedded Value는 도메인 개념상의 이유로, Entity가 초기화 되는 시점이 아닌, 별도로 생성하여 입력해주어야 하는 상황이었습니다.
그런데 Entity를 생성하려는 도중 위와같은 에러가 발생했습니다.
결론부터 말씀드리자면, Embedded Value를 초기화하면 문제는 해결됩니다. 이번 포스팅은 이 에러에 대한 원인을 찾아보고, 해결해나아가는 과정을 정리한 글입니다.
2. 예제
환경
x
name = "item") (
public class ItemEntity {
private Long itemId;
private String name;
private StockInformation stockInformation;
public ItemEntity(String name) {
this.name = name;
}
}
ItemEntity
는 아이템의 이름을 나타내는 name
필드와, 재고량을 나타내는 StockInformation
EmbeddedValue
를 가지고 있습니다.
public class StockInformation {
private int stockQuantity;
private int initStockQuantity;
}
StockInformation
은 현재 재고량을 나타내는 stockQuantity
와, 초기 재고량을 나타내는 initStockQuantity
필드를 가지고 있습니다. 중요한 점은 모든 필드가 Primitive type이라는 점입니다.
엔티티 생성 시도
public class ItemService {
private final ItemRepository itemRepository;
public void createItem(ItemController.CreateItemRequest request) {
itemRepository.save(new ItemEntity(request.getName()));
}
}
Entity를 생성하는 Service입니다. 내부의 Embedded Value는 초기화 하지 않고, name만 값을 넣었습니다.
xxxxxxxxxx
public class ItemController {
private final ItemService service;
"/item") (
public void createItem( CreateItemRequest request) {
service.createItem(request);
}
public static class CreateItemRequest {
private String name;
}
}
item생성 서비스를 호출하는 컨트롤러를 생성하고 테스트를 진행합니다.
postman을 이용해 호출합니다.
에러가 발생하는 것을 볼 수있습니다.
원인
Java에서는 기본적으로 int, float, double, byte, char, boolean
과 같은 Primitive Type은 단순 데이터를 나타내는 type이기 때문에 null값을 넣을 수 없습니다.
public class StockInformation {
private int stockQuantity;
private int initStockQuantity;
}
하지만, 저희가 생성한 StockInformation EmbeddedValue
에는 Primitive Type의 필드들 밖에 존재하지 않으며, Item 생성 서비스에서는 StockInformation을 초기화하는 코드가 없습니다.
때문에 ItemEntity안의 StockInformation은 null이 될 것이며, 뒤이어 내부의 모든 필드들은 초기화 되지 않아 null일 것입니다. 따라서, Database에는 해당 필드 값들이 null로 저장이 될것입니다.
이때 해당 Entity를 어플리케이션에서 로딩하려한다면 대상 field들이 Primitive Type이기 때문에 null값을 대입할 수 없어 Error가 발생하게 됩니다.
해결
1. Embedded Value 필드에서 초기화
name = "item") (
public class ItemEntity {
private Long itemId;
private String name;
private StockInformation stockInformation = new StockInformation();
public ItemEntity(String name) {
this.name = name;
}
첫번째 방법은 위와 같이 필드에서 Embedded Value를 초기화하는 방법입니다. StockInformation을 초기화 하게 되면, StockInformation 클래스의 인스턴스 필드들은 적절한 값으로 초기화가 될것입니다. (Ex. int = 0, ...)
해당 필드들이 초기화 되었기때문에, database에는 초기값이 저장되게 됩니다.
2. Embedded Value의 필드들을 Wrapper Class로
public class StockInformation {
private Integer stockQuantity;
private Integer initStockQuantity;
}
primitive Type 이기 때문에 발생하는 에러라면, 이 필드들을 Wrapper Class로 바꾼다면 문제는 해결될 것입니다.
필드들이 Primitive type이 아닌, class이기 때문에, null값을 넣을 수 있게되고, 데이터베이스에도 null이 저장됩니다.