[TIL] JPARepository에서 save()보다 saveAll()의 성능이 좋은 이유 feat.AOP와 프록시

이형석·2024년 9월 25일

Spring JPA Save() vs SaveAll() vs Bulk Insert
이 블로그 글을 이해하기 위해서 시작되었다.

우선, 위 글에서 이 문장이 이해가 안갔다.
save() 메서드의 특징은 @Transactional 로 감싸져 있어 프록시 기반 동작을 합니다.

여기서 프록시가 왜 나오는지?

예를 들어, Repository클래스의 save()라는 메서드에 @Transactional이 달려있음.
-> Repository객체가 save()메서드를 실행하려고 함.
-> 그런데 사실 Repository 객체는 @Transactional 애노테이션을 읽을 수 없음.
-> 그래서 @Transactional 애노테이션을 읽을 수 있는, 프록시 Repository를 생성함.
-> 따라서 프록시 Repository가 @Transactional과 save()를 실행하게 됨

알고있다시피, @Transactional은 AOP기능임. 이 AOP 애노테이션을 읽기 위해서, 프록시 객체를 생성하게 되는 것. 따라서 AOP는 프록시 패턴을 기반으로 동작.

따라서 위 블로그에 따르면, save()는 트랜잭션이 100,000번 실행되므로 프록시 Repository가 100,000번 생성된다. saveAll()은 트랜잭션이 1번이므로 프록시 Repository가 1번만 생성된다.


참고1

그리고 프록시는 리플렉션 기술로 프록시 객체를 생성한다. 이 리플렉션이라는 기술은 비용이 많이 드는 기술이므로 프록시 사용 횟수가 줄면 자연히 성능이 향상됨.

참고2

프록시 생성방식에는 JDK 동적 프록시CGLIB 프록시라는 2가지 방식이 있음.

  • JDK 동적 프록시 : 프록시객체는 인터페이스가 있는 구현클래스로만 생성가능 했음. 왜냐하면 프록시객체는 프록시관련 클래스를 상속받고 있는데, 프록시객체는 내가 사용할 객체도 상속받아야 함. 하지만 자바에서 2중상속은 불가능함. 그래서 인터페이스를 구현하고 있는 클래스에 대해서만 인터페이스를 이용해 프록시객체를 사용할 수 있었음.
  • CGLIB 프록시 : 인터페이스 없는 클래스로도 프록시 생성 가능.

CGLIB 방식이 가능한 이유 나중에 찾아보기


+ 근데 그럼 Service 클래스에서 @Transactional을 달고 해당 Repository를 사용하면, save()를 10만번 호출하든 saveAll()을 사용하든 트랜잭션은 한 번만 생기므로 성능이 똑같아지는거 아닌가?

profile
금융IT 개발자

0개의 댓글