문제 상황 : deleteAllByIdxIn 호출 시 entitymanager가 왜 없을까?
JPA OSIV라면 기본적으로 트랜잭션 범위는 서비스 단까지 있을테고, entity manager는 생성됐을 것이다. 그런데 왜 아래와 같은 에러가 났을까??
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
@Service
@AllArgsConstructor
public class ItemServiceImpl implements ItemService {
private final ItemRepository itemRepository;
@Override
public Item findById(long itemId) {
final Optional<Item> itemOptional = itemRepository.findByIdx(itemId);
return itemOptional.get();
}
@Override
public void delete(long itemId) {
final int num = itemRepository.deleteAllByIdxIn(Lists.newArrayList(itemId));
System.out.println(num);
}
}
JPA EntityManager는 누가 열까?
아래 화면들은 DELETE API를 호출 시에 Entity Manager가 생성되는 코드와 breakpoint이다.
public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAccessor implements AsyncWebRequestInterceptor {
// ...
@Override
public void preHandle(WebRequest request) throws DataAccessException {
String key = getParticipateAttributeName();
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
if (asyncManager.hasConcurrentResult() && applyEntityManagerBindingInterceptor(asyncManager, key)) {
return;
}
EntityManagerFactory emf = obtainEntityManagerFactory();
if (TransactionSynchronizationManager.hasResource(emf)) {
// Do not modify the EntityManager: just mark the request accordingly.
Integer count = (Integer) request.getAttribute(key, WebRequest.SCOPE_REQUEST);
int newCount = (count != null ? count + 1 : 1);
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
}
else {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewInterceptor");
try {
EntityManager em = createEntityManager();
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(emf, emHolder);
AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(emf, emHolder);
asyncManager.registerCallableInterceptor(key, interceptor);
asyncManager.registerDeferredResultInterceptor(key, interceptor);
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
}
}
}
}
deleteAllByIdxIn 동작 방식
- EntityManager opened
- SELECT 쿼리 생성
- EntityManager closed
- DELETE 쿼리 예외 발생
EntityManager 없다는 에러를 해결하기 위해 @Transactional 붙여서 로그보기
JpaRepository에서 기본적으로 함수들은 @Transactional이 붙은줄 알았는데 아닌가....?
SimpleJpaRepository 코드를 살펴보자
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
내가 알고 있던 대로 SimpleJpaRepository에는 Transactional 어노테이션이 붙어있다. 하지만, 구현 함수중에 deleteAllBy는 찾을 수가 없었다.
@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {
@Transactional
@Override
public void deleteAll(Iterable<? extends T> entities) {
즉 deleteAllBy...으로 사용하면 트랜잭션 어노테이션이 기본적으로 먹히지 않는것 같다. 그래서 아래와 같이 했더니 해결은 되긴 했다. 좀더 깊은 이해가 필요할 것 같다....
public interface ItemRepository extends JpaRepository<Item, Long> {
@Transactional
int deleteAllByIdxIn(List<Long> ids);
}
'Back-End > Spring' 카테고리의 다른 글
스프링 blocking vs non-blocking : R2DBC vs JDBC & WebFlux vs Web MVC (1) | 2020.07.01 |
---|---|
트랜잭션 전파 속성 ( propagation ), 롤백 예외 (0) | 2020.01.30 |
Spring Boot Test 및 심화 (0) | 2020.01.08 |
Spring을 이용하여 개발할 때 고민, 클린코드 짜기, 코드리뷰 항목 (0) | 2020.01.02 |
Spring @Order 어노테이션 (0) | 2019.11.28 |