[JPA] Spring Data JPA의 save의 동작 과정

박진형·2021년 11월 23일
0

JPA

목록 보기
3/7

Spring Data JPA에서 제공하는 save() 함수는 어떻게 구현되어 있고 어떻게 동작하는지 정리해보려고한다.

  • SimpleJpaRepository.save()
	@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);
		}
	}

Persist vs Merge

코드를 보면 저장하려는 엔티티가 새로운 엔티티인지 아닌지 확인해서 persist를 하거나 merge를 한다.

persist와 merge의 차이는 select 쿼리를 하지않고, 하고의 차이가 있다.
즉, 새로운것이면 db에서 찾을필요 없이 넣어주면 되는 것이고, 새로운것이 아니면 db에서 찾아내어 '변경'을 해주는 것이다.

그러므로 성능에서 어느정도 차이가 난다고 볼 수 있다.

새로운 Entity 구별법

isNew() 함수는 Entity의 ID가 null인지 아닌지 구별을 해서 null이라면 새로운 Entity로 간주하고 null이 아니라면 이미 존재하는 Entity로 간주한다.

참고로 @Id @GeneratedValue 어노테이션을 사용한 ID인 경우 persist()를 호출한 이후 ID가 생성된다.

그런데 이런 구조에 의해 문제가 발생하는 경우가 있다.

Entity의 ID로부터 발생하는 문제

@Id @GeneratedValue
private Long id;

Entity의 ID를 @GeneratedValue로 위와 같이 자동으로 생성하지 않고 아래와 같이 임의로 정해야 하는 경우 문제가 발생한다.

@Id
private String id;

위와 같은 경우에는 자동으로 생성하지 않기 때문에 persist()를 호출하기전에 ID에 값을 정해줘야한다.

그러면 save()를 호출할 때 문제가 발생한다.

위에서 말했듯이 save()는 새로운 Entity인지 구별하는 방법은 ID가 null인지의 여부를 통해서 구별한다.

하지만 임의로 ID를 입력할 경우에는 새로운 Entity로 간주되지 않아 merge를 통해서 동작하고 db에서 해당 entity를 찾아내려고 select 쿼리가 나간다.
DB에 존재하지도 않는 entity를 찾아내려고 하는 것이다. JPA는 DB에 select를 통해 찾아내려고 하는 것이고, 없다는것을 확인하고 그제서야 insert를 한다.

이런 불필요한 동작과정은 성능상 문제가 될 수 있으므로 해결을 해야한다.

해결방법

@CreatedDate 어노테이션과 Persistable 인터페이스 를 사용

@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는 entity가 persist 될 때 자동적으로 입력이 된다.

Persistable 인터페이스를 상속받아 isNew()함수와 getId()함수를 오버라이딩하면
Spring Data JPA의 save 함수에서 해당 isNew()함수를 사용하게 된다.

@createdDate는 최초 persist된 시점에만 값이 정해지기 때문에 Id를 대신해서 새로운 Entity인지 아닌지 판단할 수 있는 척도가 되는 것이다.

0개의 댓글