No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call 에러.. 커스텀 메서드

lsy·2022년 12월 15일
2

발단

ParsingMenu 클래스의 saveEblockMenu() 메서드에 있는deleteAllByRestaurantPropertyId() 메서드가 해당 에러로 작동하지 않았다.

해당 메서드는 JPA 커스텀 메서드다.

해결 과정

분명히saveEblockMenu()@Transactional 을 선언했는데 동작하지 않는다.. 왜 이럴까?

답은 @Transactional의 동작 원리에 있었다.

위의 saveEblockMenu() 메서드는 (1)getDataAndSaveToDatabase() -> (2)getDataAndSaveToDatabaseEblock() -> (3)saveEblockFileName() -> (4)saveEblockMenu() 순으로 호출된다.

근데 나는 맨 처음 호출하는 메서드인 (1)getDataAndSaveToDatabase()@Transactional를 추가하지 않았다.

호출하는 메서드에 @Transactional이 적용되어 있지 않으니까 당연히 하위 메서드들도 적용(전파)되지 않는다.

따라서 (1)getDataAndSaveToDatabase()메서드를 트랜잭션으로 감싸주는 프록시 객체가 만들어지지 않고, EntityManager가 존재하지 않는다는 에러가 난 것이다.

그런데 이상하다. 중간에 (3)saveEblockFileName()에서도 JPA의 save를 사용하는데 에러가 나지 않고 왜 (4)saveEblockMenu()의 delete에서만 에러가 날까?

해당 메서드 내용이다. 여기서도 EntityManager가 필요한 save를 사용하고 있다. 그러나 이 부분에서는 에러가 나지 않는다.

한 번 JpaRepository의 defalut 구현체인 SimpleJpaRepository를 살펴보자.

@Transactional이 붙어있다. 따라서 에러가 나지 않았던 것이다.

이것이 무슨 소리냐 하면..

나는

(1)getDataAndSaveToDatabase() -> (2)getDataAndSaveToDatabaseEblock() -> (3)saveEblockFileName() -> (4)saveEblockMenu() 순으로 메서드를 호출했다.

그렇다면 내가 호출한 메서드는ParsingMenu::getDataAndSaveToDatabase()이며 이 메서드는 트랜잭션으로 감싸져 있지 않다. 따라서 영속성 컨텍스트가 필요한 작업이 있으면 에러가 날 것이다.

하지만 (3)saveEblockFileName()에서는 영속성 컨텍스트가 필요한 작업인 save를 사용해도 에러가 나지 않았다. 그 이유는 (3)saveEblockFileName()에서 JpaRepository::save()를 호출했기 때문이다.

주의할 점은 ParsingMenu의 메서드를 호출한게 아니다. JpaRepository 구현체의 save()메서드를 호출했다. 그리고 이 메서드는 @Transactional이 적용되어 있다. 따라서 트랜잭션으로 감싸진 프록시 객체를 사용할 것이다. 당연히 에러가 나지 않는다.

그런데 (4)saveEblockMenu()deleteAllByRestaurantPropertyId()는 내가 만든 커스텀 메서드다. 이 메서드는 나중에 동적 프록시에 의해 분석되어 구현되고 JpaRepository::deleteAllByRestaurantPropertyId()로 실행된다. 하지만 중요한건 이 메서드엔 @Transactional이 자동으로 적용되지 않는다는 점이다.

따라서 해당 메서드에 @Transactional을 적용시키면 (1)getDataAndSaveToDatabase()@Transactional을 적용시키지 않아도 해결된다.

결론

내 상황에서는 해결 방법이 세 가지가 있을 것 같다.

  1. 직접 호출하는 메서드(최상위 메서드)에 @Transactional를 적용시켜 전파시킨다.
  2. 만약 그러기 곤란한 상황이고 이 상황이 커스텀 메서드 때문이라면 해당 메서드에 @Transactional를 적용시킨다.
  3. 그냥 둘 다 적용시킨다.

나는 3번을 택했다.

profile
server를 공부하고 있습니다.

1개의 댓글

comment-user-thumbnail
2024년 7월 6일

트렌젝션을 적용하지 않아도 잘 작동하는 기본 메서드에 대해 의문을 갖고 있었는데 정말 감사합니다.

답글 달기