Server/Spring JPA

Spring Data JPA - JpaRepository 구현체에 @Transactional(readOnly = true)가 있는 이유

JaeHoney 2022. 4. 10. 11:33

 

JpaRepository

Spring Data JPA를 사용할 때 JpaRepository를 상속하는 interface를 만들어서 사용합니다.

public interface MemberRepository extends JpaRepository<Member, Long> {
}

우리는 interface를 상속했을 뿐인데, findAll() 같은 다양한 CRUD 메서드를 사용할 수 있습니다.

memberRepository.findAll();

 

Spring Data JPA는 내부적으로 JpaRepository를 상속하기만 해도 아래와 같은 구현체를 생성해주기 때문입니다.

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

    @Override
    public List<T> findAll() {
	    return getQuery(null, Sort.unsorted()).getResultList();
    }
    
    // 다양한 CRUD 메서드 (생략) ...
}

JpaRepository구현체에 @Transactional(readOnly = true)가 있습니다.

 

단순히 읽는 용도면 트랜잭션이 왜 필요한걸까요??

@Trasnactional

궁금해서 검색을 조금 해보니, 성능 향상을 위해서 @Transactional(readOnly = true)를 사용한다는 글이 많습니다.

 

절반은 사실이지만, 정확한 표현이 절대 아닙니다!

 

트랜잭션 없이도 쿼리를 실행할 수는 있습니다. 하지만, 이 때는 영속성 컨텍스트는 작동하지 않습니다.

 

JPA의 엔터티는 반드시 영속성 컨텍스트(Persistance Context)안에서 동작합니다. 영속성 컨텍스트(Persistance Context)는 트랜잭션에서 동작합니다.

 

JPA의 Entity Manager는 기본적으로 PersistenceContextType.TRANSACTION 로 사용됩니다.

 

결과적으로, JPA의 Entity Manager는 트랜잭션이 있어야 동작합니다.

 

1차 캐시를 사용할 생각이 없다면, Read 메소드에서는 Transaction이 없어도 무방합니다. Spring Data JPA는 영속성 컨텍스트를 사용하기 위해서 모든 메소드에 기본적으로 트랜잭션을 생성합니다.

 

(readOnly = true)

@Transactional(readOnly =true)에서 readOnly 속성을 true로 명시하는 이유는 성능 향상이 맞습니다.

 

기본적인 @Transactional은 스냅샷을 저장하고, 변경을 감지하게 됩니다.

 

읽기(Read)를 위한 Repository method가 변경감지를 하는 것 자체가 성능 낭비이기 때문에, readOnly 속성을 주는 것입니다.

 

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

    @Override
    public Optional<T> findById() {
        // ...생략
    }
    
    @Transactional
    @Override
    public void deleteById(ID id) {
        // ...생략
    }
    
    // ...
}

deleteById()처럼 변경(Write)에 대한 메소드들은 @Transactional을 명시합니다. 더 좁은 범위의 애노테이션이 적용되고 클래스에 적용된 @Transactional(readOnly = true)는 무시합니다.

 

반면, 읽기(Read)에 대한 메소드들은 method 단위의 @Transactional을 명시하지 않아서 Class 단위의 @Transactional(readOnly = true) 속성이 적용됩니다.

 

모든 읽기 메소드에 @Transactional(readOnly = true) 속성을 정의해도 문제가 없지만, 읽기(Read) 메소드가 훨씬 많습니다.

 

그래서 클래스 단위로 @Transactional(readOnly)를 먼저 명시하고, 수정(Write) 메소드에 한해서 새로 @Transactional을 명시해준다고 보시면 되겠습니다!

 

감사합니다.