스프링 데이터 JPA 구현체 분석
스프링 데이터 JPA는 공통 인터페이스의 구현체로 SimpleJpaRepository를 제공합니다. 이 클래스는 기본적인 CRUD 연산을 포함한 다양한 메서드를 구현하고 있습니다.
SimpleJpaRepository 클래스
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepository<T, ID> {
// ... other methods ...
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
// ... other methods ...
}
- @Repository: JPA 예외를 스프링이 추상화한 예외로 변환합니다.
- @Transactional: 트랜잭션 처리를 담당합니다. 모든 JPA 변경은 트랜잭션 안에서 동작해야 합니다.
트랜잭션 처리
스프링 데이터 JPA는 변경(등록, 수정, 삭제) 메서드를 자동으로 트랜잭션 처리합니다. 서비스 계층에서 트랜잭션이 시작되지 않으면, 리포지토리에서 트랜잭션을 시작합니다. 서비스 계층에서 트랜잭션을 시작하면, 리포지토리는 해당 트랜잭션을 전파 받아서 사용합니다.
@Transactional(readOnly = true)
단순 조회용 트랜잭션에서는 readOnly = true 옵션을 사용하면, 플러시를 생략하여 약간의 성능 향상을 얻을 수 있습니다.
save() 메서드
- 새로운 엔티티면 저장(persist)
- 새로운 엔티티가 아니면 병합(merge)
새로운 엔티티를 구별하는 방법:
- 식별자가 객체일 때 null로 판단
- 식별자가 자바 기본 타입일 때 0으로 판단
Persistable 인터페이스
새로운 엔티티 여부를 직접 판단하고 싶으면, Persistable 인터페이스를 구현하여 판단 로직을 변경할 수 있습니다.
package org.springframework.data.domain;
public interface Persistable<ID> {
ID getId();
boolean isNew();
}
Persistable 구현 예제
JPA 식별자 생성 전략이 @GeneratedValue인 경우, save() 호출 시점에 식별자가 없으므로 새로운 엔티티로 인식하여 정상 동작합니다. 하지만 식별자 생성 전략이 @Id만 사용해서 직접 할당하는 경우에는 이미 식별자 값이 있는 상태로 save()를 호출하기 때문에 merge()가 호출됩니다. 이 경우 merge()는 DB를 호출하여 값을 확인하고, DB에 값이 없으면 새로운 엔티티로 인지하므로 비효율적일 수 있습니다.
이 문제를 해결하기 위해 Persistable 인터페이스를 구현하여 새로운 엔티티 여부를 판단합니다. 예를 들어, @CreatedDate를 사용하여 새로운 엔티티 여부를 확인할 수 있습니다.
package study.datajpa.entity;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.domain.Persistable;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id;
import java.time.LocalDateTime;
@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
@Id
private String id;
@CreatedDate
private LocalDateTime createdDate;
public Item(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public boolean isNew() {
return createdDate == null;
}
}
이렇게 하면 @CreatedDate가 null인지 여부로 새로운 엔티티인지 판단할 수 있습니다. 새로운 엔티티일 경우 persist가 호출되고, 기존 엔티티일 경우 merge가 호출됩니다.
정리
- SimpleJpaRepository는 스프링 데이터 JPA의 기본 구현체로, CRUD 연산을 처리합니다.
- @Repository와 @Transactional을 사용하여 예외를 추상화하고 트랜잭션을 관리합니다.
- save() 메서드는 새로운 엔티티 여부를 판단하여 persist 또는 merge를 호출합니다.
- 새로운 엔티티 여부를 판단하기 위해 Persistable 인터페이스를 구현할 수 있습니다. 이를 통해 효율적인 엔티티 관리를 할 수 있습니다.
이와 같이 스프링 데이터 JPA의 구현체를 이해하고 활용하면, 보다 효율적인 데이터 처리를 할 수 있습니다.
'JPA 실전' 카테고리의 다른 글
[JPA] JPA 실전 - 확장 기능 (0) | 2024.07.02 |
---|---|
[JPA] JPA 실전 - 쿼리 메소드 (3) (0) | 2024.07.02 |
[JPA] JPA 실전 - 쿼리 메소드 (2) -페이징과 정렬 (0) | 2024.07.02 |
[JPA] JPA 실전 - 쿼리 메소드 (1) (0) | 2024.07.02 |
[JPA] JPA 실전 - 공통 인터페이스 (1) | 2024.06.30 |