요약
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하는 기능이였는데
두개는 같은 트랜잭션으로 묶이지 않으면...
큰일이 날 것 같다...( •ˍ• ;)