20190528

1. @Transactional

회사 업무 중, 테스트 환경에서 적절하게 @Transactional을 부여하지 못해서 실수한 부분이 있어서 메모하려고 한다.

Repository를 의존하고 있는 서비스 레이어에서 명시적으로 @Transactional을 서비스 매서드에 마킹 해놓지 않은 상태에서 해당 서비스 메서드의 호출이 종료된 시점에 @OneToMany(FetchType.Lazy)로 마킹된 하위 모델 엔티티가 조회를 시도하게 되면서 생기는 문제였다.

기본적으로 Data JPA에서의 Repository는 CRUD 오퍼레이션에 대해 트랜잭션을 가져가게 되는데, 문제가 되는 상황은 엔티티에서 FetchType.Lazy를 사용했을 때, 즉 지연 로딩을 사용할 때가 문제가 되는 듯 했다. 만약 레파지토리에서의 트랜잭션을 재정의하고 싶다면 아래와 같이 재정의를 하면 되고, 또한 readOnly 와 같은 속성을 사용하여 Replication 구조상에서 해당 트랜잭션이 Read Replication을 참조하도록 변경하는 것도 가능하다.

1
2
3
4
5
6
public interface UserRepository extends CrudRepository<User, Long> {

@Override
@Transactional(timeout = 10)
public List<User> findAll();
}

또한, 만약 클래스 레벨에서의 @Transactional의 속성 값이 readOnly=true로 지정되어 있고, 매서드가 READ 오퍼레이션이 아닌 CUD 오퍼레이션을 취한다면 아래와 같이 @Modifying을 붙여서 트랜잭션을 재정의 하는 것이 가능하다고 한다.

1
2
3
4
5
6
7
8
9
10
@Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {

List<User> findByLastname(String lastname);

@Modifying
@Transactional
@Query("delete from User u where u.active = false")
void deleteInactiveUsers();
}