( 프로젝트 진행하면서 관련 내용 꼭 써야지 했었는데... 미루고 미뤘던 내용을 오늘 써보려고 한다 )
프로젝트 1차 마무리 이후, 코드가 많이 별로였다는 것을 느끼고 리팩토링을 진행중이다 😥
처음 Spring 사용하면서 @Transactional에 대해 신경을 안 썼었고, 후반에 공부하면서 신경을 쓰게 된 후에 @Transactional의 변경감지에 의해 자동으로 commit되는 부분을 위주로 리팩토링 하는 과정이 있었다.
알아서 변경감지에 의해 DB에 update되지만 save코드를 굳이굳이 넣었던 그런 코드와 그렇지 않고 save를 통해 insert해야하는 코드를 들여다 보다가 save와 saveAll에 대한 차이점에 대한 고찰을 하게 되었다.
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "Entities must not be null!");
List<S> result = new ArrayList();
Iterator var3 = entities.iterator();
while(var3.hasNext()) {
S entity = var3.next();
result.add(this.save(entity));
}
return result;
}
처음에는 두 코드를 보고 그니까.. 어.. save를 반복한게 그냥 saveAll인가? 싶었다.
그래서 비교해보았다. 얼마나 차이가 나는지
save()
saveAll()
비교해본 결과, List를 entity에 담아서 한 번에 saveAll하는 것이 좋다는 것은 자명한 사실이라는 것을 알았다.
그렇다면.. 어느 점에서 다른것일까?
@Transactional이 붙은 클래스는 프록시로 빈으로 등록된다.
save와 saveAll메소드 모두 @Transactional이 붙어있는데, 여기서 빠른 이유가 결정난다.
→ 이 함수를 호출해야 인터셉트되어서 트랜잭션으로 묶인다. 그래서 Bean객체 안에서 내부 함수로 호출하게 되면, @Transactional이 적용이 되지 않는다.
saveAll() 메소드에서 save를 호출할 때, this.save(entity)
와 같이 this로 호출하기 때문에 내부 메소드를 호출한 효과를 주게 되어, 프록시 참조를 통한 호출과 다르게 동작한다.
따라서 기존 생성된 transactional에 계속 참여하는 형태로 진행되어 비용이 들지 않기 때문에 더 빠르다.
( 반대로 save는 그 만큼의 프록시 과정을 거치게 되어 새로운 트랜잭션 생성과정을 계속 거치게 되어 느리다 )
대량의 데이터를 저장할 때는 하나의 트랜잭션만을 만들어서 쿼리를 일괄적으로 모아서 보내는 saveAll 메서드가 더 합리적이군요!!