[JPA] JPA Repository 수정,삭제와 트랜잭션

타미·2020년 10월 5일
0

JPA실험실

목록 보기
4/8

💫상황

JPA에서 제공하는 Repository로 delete 관련 메서드를 만들었다.

public interface InstagramRepository extends JpaRepository<Instagram, Long> {
    void deleteByPlaceId(Long placeId);
}

그리고 관련 로직을 수행하면 예외가 발생했다.


@Transactional
public doSomething(Instagram instagram, Place place) {
	delete(instagram, place);
    save(instagram);
}

private delete(Instagram instagram, Place place) {
        if (어떤 조건이라면) {
            instagramRepository.deleteByPlaceId(crawlingPlace.getId());
        }
}

대략적으로 이런 상황이었다. 그리고 테스트를 돌리면 deleteByPlaceId 에서 Transaction이 있는 EntityManager가 없다는 예외가 발생했다.

No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call

doSomething 메서드에서 트랜잭션을 걸어줬으면 전파되야 하는 것 아닌가?

💫 문제와 해결방법

트랜잭션은 private으로 전파되지 않는다.

delete 메서드는 외부에서 사용하지 않기 때문에 private으로 막아놨다. 하지만 private 메서드는 트랜잭션이 걸리지 않는다. 그래서 트랜잭션이 걸려있지 않아 트랜잭션이 있는 EntityManager가 없다는 예외가 발생한 것이다.

public delete(Instagram instagram, Place place) {
        if (어떤 조건이라면) {
            instagramRepository.deleteByPlaceId(crawlingPlace.getId());
        }
}

Custom Delete,Save에는 트랜잭션을 붙이자.

위와 같이 private에서 public으로 바꾸면 트랜잭션이 전파되면서 예외가 발생하지 않는다. 하지만 이런 식으로 처리한다면 예상치 못한 상황에서 예외가 발생할 수 있다. 부모에 항상 트랜잭션을 달아주는 것은 번거롭다.

public interface InstagramRepository extends JpaRepository<Instagram, Long> {
	@Transactional
    void deleteByPlaceId(Long placeId);
}

그래서 위와 같이 조회가 아닌 저장,수정,갱신 로직에는 트랜잭션을 붙여주는 게 좋다.
JpaRepostiroy의 실제 구현체인 SimpleJpaRepository 속을 들여다 보면 save, delete 관련 로직에는 트랜잭션이 붙어있다. 실제 구현체에서 제공하는 save,delete 메서드가 아니라면 트랜잭션을 붙여주자!

profile
IT's 호기심 천국

0개의 댓글