티스토리 뷰

jpa

코틀린답게 JPA Entity 작성하기

songjb 2023. 11. 2. 00:35

코틀린답게 JPA Entity 작성하기

@Entity
public class Parents {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
		...
}

 

자바에서는 일반적으로 GeneratedValue를 사용해 id를 설정하며, 별도로 id 값을 초기화하지 않습니다.

@Entity
data class Parents(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long?,
) {
}

 

하지만 코틀린의 Null Safety 특성 때문에 id를 초기화하지 않고 null 상태로 둘려면 ?를 사용하여 null을 허용해야 합니다.

 

이는 코틀린의 Null Safety 특성을 제대로 활용하지 못하는 것 같았습니다.

 


 

val  id: Long = 0L

 

코드를 참고하다가 id를 0L로 초기화하는 방식을 발견했습니다.  

지금까지는 id가 null일 경우에만 새로운 entity로 간주하여 persist()를 호출하고, id가 0L로 초기화된 경우는 기존 entity로 판단하여 select 비용이 발생한다고 생각했습니다. 이로 인해 해당 방식이 비효율적이라고 느꼈습니다.  

지금까지 관습적으로 Id를 설정하였는데, 이에 대해 궁금증이 생겼습니다.


 

Id가 Null 인 경우

 

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Posts {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
}

@DataJpaTest
class PostsTest {

    @Autowired
    private PostRepository postRepository;

    @Test
    @DisplayName("id가 null일 경우 새로운 Entity로 간주하고 persist 한다.")
    void test() {
        Posts posts = new Posts(null,"제목");
        postRepository.save(posts);
    }
}

 

새로운 entity로 간주하고 perist 하는것을 볼수있다.


 

Id가 0L인 경우

 

 

동일한 코드 생성자 id에 0L을 넣고 테스트를 진행한 결과 기존 entity로 간주하고 merge 하는것을 볼수있다.


 

새로운 엔티티 식별 방법 isNew

 

위와 같이 isNew 메서드에서 id 타입이 primitve type인지 확인하고  primitve type 이 아닌 wrapper type 의 경우 null 인경우만 새로운 entity로 판단한다.


 

코틀린에서는?

 

코틀린은 원시 타입(primitive type)과 래퍼 타입(wrapper type)을 따로 구분하지 않습니다.

코틀린의 타입은 컴파일 시 자바의 primitive 또는 wrapper 타입으로 자동 변환됩니다.
런타임 시점에 숫자 타입은 가능한 한 가장 효율적인 방식으로 표현이 되며, 예를 들면 대부분의 경우 코틀린의 Int 타입은 자바의 int 타입으로 컴파일됩니다. 다만, Collection이나 Generic을 사용하는 경우에는 wrapper로 변경되고 그 외 나머지 경우에는 int로 변경됩니다. 예를 들어, Int 타입의 Collection을 만드는 경우 래퍼 타입에 해당하는 java.lang.Integer 객체가 들어가게 되며 이 부분은 자바에서 프로그래밍을 할 때와 동일합니다.
https://0391kjy.tistory.com/57

 


 

코틀린->자바 변환

 

변환 전

@Entity
data class Announcement(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long,
    val title: String,
    @Lob
    val content: String,
    @ManyToOne
    @JoinColumn(name = "memberId")
    val member: Member
) {

    constructor(title: String, content: String, member: Member)
            : this(0L, title, content, member)
}

 

변환 후

@Entity
public final class Announcement {
   @Id
   @GeneratedValue(
      strategy = GenerationType.IDENTITY
   )
   private final long id;
   @NotNull
   private final String title;
   @Lob
   @NotNull
   private final String content;
   ...
}

 

즉 코틀린에서는 Long을 자바로 변환시 primitive type의 long으로 변환 되기때문에 0으로 값을 초기화해줘도 persist 된다.