javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'flush' call

hanana·2023년 12월 6일

요약

EntityManager 직접 호출해서 영속성컨텍스트를 관리해줄 경우
트랜잭션 범위를 제대로 파악하고 사용하자

SpringDataJPA를 활용하던중
EntityManager 에 flush 할 수 없다는 에러를 보았다.


문제가 된 코드

public String excelReg(List<AddItemReg> dtos) {
    repository.deleteAllByCode({Entity}.getCode());
    // 벌크연산(delete) 후에 영속성컨텍스트 초기화
    em.flush();
    em.clear();
    dtos.forEach(e -> {
        // 생략
    });
    return "success";
}

원인은 생각보다 간단했다.

@Transactional // 이거추가!!
public String excelReg(List<AddItemReg> dtos) {
    repository.deleteAllCode({Entity}.getCode());
    // 벌크연산(delete) 후에 영속성컨텍스트 초기화
    em.flush();
    em.clear();
    dtos.forEach(e -> {
        // 생략
    });
    return "success";
}

문제가 된 애플리케이션의 코드는 대부분 QueryDsl에 Dto로 쿼리하여 기능에 맞게 fit하게 개발되어 있었고,
이러한 이유 때문이였는지 Service 레이어에 @Transactional 어노테이션이 아닌 repository 레이어에 @Transactional이 붙어있었다.

즉, deleteAll 코드가 실행된 후에 영속상태가 끊어졌는데
영속성컨텍스트를 초기화 하려고 하니까 발생한 에러였다.
@Transactional 범위를 재지정하여 문제를 해결하였다.


여담

코드의 일관성을 위해서 나도 가급적이면 repository 레이어에 @Transactional 어노테이션을 붙이고 있었는데
생각보다 훨씬 위험한 발상이였던것 같다.

문제의 코드는 일단 데이터를 전부 delete하고
새로운 데이터를 insert하는 기능이였는데

두개는 같은 트랜잭션으로 묶이지 않으면...
큰일이 날 것 같다...( •ˍ• ;)

profile
성숙해지려고 노력하지 않으면 성숙하기까지 매우 많은 시간이 걸린다.

0개의 댓글