본문 바로가기
Spring Data

Entity는 기본형 타입과 래퍼클래스 중 어떤 것을 사용하는가?

by 코더 제이콥 2023. 1. 29.

엔티티의 필드는 어떻게 선언해야 하나?


엔티티에서 필드를 선언할 때 기본형으로 선언해야하는지, 래퍼 클래스(Wrapper Class)로 선언해야 하는지 고민될 때가 있습니다. 저는 @Id가 매핑된 필드에는 무조건 참조형 타입을 선언하길래 왜 기본형을 사용하지 않는지 고민했었는데요? 아래 사실들을 알면 납득되실 겁니다.

 

@Id @GeneratedValue
private Long id;

 

래퍼 클래스는 Null을 허용한다.


Integer, Long, String, Boolean, Double 등등... 래퍼 클래스는 기본형과 달리 Null을 허용합니다. 반대로 기본형 타입은 Null을 허용하지 않죠. 이 둘간의 차이점을 바탕으로 생각을 하셔야 합니다.

 

엔티티의 본질은 무엇인가?

엔티티는 하나의 정체성을 가진 객체로 속성(Attribute)의 변경과 무관합니다. 엔티티에는 사용자와 관련된 오래 지속되는 정보들을 담고 있으며, 주로 데이터베이스에 저장됩니다.

 

JPA에서 엔티티의 역할은 무엇인가?

JPA에서 엔티티들은 POJO(https://ko.wikipedia.org/wiki/Plain_Old_Java_Object)일 뿐이며, 데이터베이스에 영속될 수 있는(persisted) 데이터를 나타낼 뿐입니다. 그리고 엔티티들은 데이터베이스에 저장되는 테이블에 해당됩니다. 또 모든 엔티티 인스턴스들은 테이블의 행(Row)를 나타냅니다. (엔티티의 필드는 열이겠죠?)

 

JPA에서 필드 선언은 어떻게 해주면 될까?


결론부터 말하자면, 값에 Null이 들어가야 한다면 래퍼 클래스, 아니라면 기본형으로 작성합시다. 또한 식별자(Id)는 래퍼클래스를 사용합시다. 이는 하이버네이트(Hibernate)의 권장 사항이기도 합니다. 하이버네이트에서는, 식별자에 Null이 허용되는 타입으로 필드를 선언할 것을 권장하고 있습니다. 즉, 래퍼 클래스로 식별자 타입을 쓸 것을 권장하는 것입니다. (Hibernate 참조하기)

 

ID를 Wrapper 클래스로 설정해야 할 이유는 또 있습니다. JpaRepository를 구현한 SimpleJpaRepository를 봐봅시다. 인텔리제이는 CTRL + N을 누르고 검색 가능합니다. SimpleJpaRepository에서 기본적인 CRUD를 구현하고 있는데, 한번 save 메소드를 봐봅시다.

 

/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
@Transactional
@Override
public <S extends T> S save(S entity) {

    Assert.notNull(entity, "Entity must not be null.");

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

 

if문을 보시면 entityInformation의 메소드 isNew함수에 entity를 넣고 있습니다. isNew이니 엔티티가 새것이면 true, 아니면 false를 반환하리라 예상됩니다. 구현 코드로 가보겠습니다.

 

/*
 * (non-Javadoc)
 * @see org.springframework.data.repository.core.EntityInformation#isNew(java.lang.Object)
 */
public boolean isNew(T entity) {

    ID id = getId(entity);
    Class<ID> idType = getIdType();

    if (!idType.isPrimitive()) {
        return id == null;
    }

    if (id instanceof Number) {
        return ((Number) id).longValue() == 0L;
    }

    throw new IllegalArgumentException(String.format("Unsupported primitive id type %s", idType));
}

 

isNew 메소드를 보시면 아시겠지만 id가 null인지 아닌지 비교하고 있습니다.

 

다시 save 메소드를 봅시다. if문을 통해 em.persist가 호출되거나, em.merge가 호출됩니다. 그리고 이것은 isNew 메소드에 좌지우지 됩니다. JPA에서 merge 메소드의 사용은 조심해야 하기 때문에, 식별자 타입은 래퍼 클래스로 선언하는 것이 맞겠습니다.

 

참조 :
https://en.wikipedia.org/wiki/Entity
https://www.baeldung.com/jpa-entities