프로젝트를 진행하면서 save() 메서드와 saveAll() 메서드를 사용했고, saveAll() 메서드가 더 빠르다는 사실도 알고있다.
하지만 정확한 원인을 모르고 있었는데, 왜 빠를까? 라는 의문이 자꾸 생기면서 찾아보게 되었다.
Spring JPA 를 활용한 데이터 저장을 위해 필요한 메서드에는 save() 와 saveAll() 메서드가 있다.
save() 메서드는 단일 데이터를 저장할 경우 그리고 saveAll() 메서드는 여러 데이터를 iterable 한 곳에 모아 한번에 저장할 때 사용한다.
그렇다면 save() 와 saveAll() 중 어떤 메서드가 같은 양의 데이터를 저장할 때, 빠를까?
SimpleJpaRepository.class
@Transactional
public <S extends T> S save (S entity) {
Assert.notNull(entry, "Entry must not be null");
if (this.entityInformation.isNew(entity)) {
this.entityManager.persist(entity);
return entity;
} else {
return (S) this.entityManager.merge(entity);
}
}
SimpleJpaRepository.class
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entry, "Entry must not be null");
List<S> result = new ArrayList();
for (S entity : entities) {
result.add(this.save(entity));
}
return result;
}
위 save() 와 saveAll() 의 구현 코드를 보면 Transactional 어노테이션이 공통적으로 붙어있는걸 확인할 수 있다.
이 Transactional 어노테이션에는 설정할 수 있는 속성들이 존재하는데, 이 속성들을 간단하게 살펴보면...
위 속성에서 중요하게 봐야할 것은 propagation인데, 이 속성의 기본 값은 REQUIRED로 설정되어있다.
위 설명을 토대로 Transactional 어노테이션 속성이 REQUIRED 로 설정되면 부모 트랜잭션이 있는지 확인해야하는 시간이 필요
이 시간의 차이가 save 와 saveAll 메서드의 차이이다.
save() 의 경우 단일 데이터를 저장하는 메소드이므로, 여러 건을 저장하기 위해선 for 문을 돌려야하는데!
1. 부모 트랜잭션이 있는지 확인
2. save() 실행
X 저장하고 싶은 데이터의 양만큼 반복
위의 과정을 저장하고 싶은 양만큼 반복해야하지만,
saveAll() 의 경우 다량의 데이터를 저장하는 메소드 이므로,
1. 부모 트랜잭션이 있는지 확인
2. saveAll() 실행
위의 과정을 딱 1번만 실행하면 되므로 시간을 아낄 수 있다.
그러므로 saveAll() 메서드가 빠른 이유이다.
saveAll() 이 구현된 코드를 보며 나도 들었던 생각이다.
이 궁금증을 해결하기 위해서는 위 save 와 saveAll 코드를 작성할 때 첫줄에 적은 클래스 명을 기억해야하는데..
Transactional 어노테이션의 경우 AOP 프록시 기반으로 구성되어 있지만, 같은 파일에서는 AOP 적용이 안되기 때문에 saveAll 메서드 내부의 save의 Transactional 어노테이션이 적용되지 않아 위의 설명처럼 1번만 구성된다.
public <S extends T> S save (S entity) {
Assert.notNull(entry, "Entry must not be null");
if (this.entityInformation.isNew(entity)) {
this.entityManager.persist(entity);
return entity;
} else {
return (S) this.entityManager.merge(entity);
}
}