javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
@Transational
어노테이션이 JpaRepository 메소드까지 적용되지 않아 발생한 오류지난 며칠간 나를 괴롭혔던 에러이다. 해결책을 검색해보면 @Transactional
어노테이션을 붙이라고 하는데, 나는 서비스 인터페이스에 작성해두었으니 작동이 되어야 한다고 생각했다.
@Transactional
은 EntityManager
가 작업할 때 적용되어야 할 속성인데, entityManager가 직접 작동하는 JpaRepository 메소드 단까지 해당 속성이 적용되지 않아 발생하는 문제인 것 같았다. (정확히 이해는 못함)
entityManager가 DB 데이터를 조작하는 경우 @Transactional
이 붙어야 연결을 생성하고 쿼리를 실행한 후 커밋, 닫기까지 과정이 실행된다고 이해했다.
그런데 서비스 인터페이스-서비스 구현체-리포지토리 인터페이스-리포지토리 구현체의 과정을 거치며 해당 속성이 끝까지 적용되지 않아 문제가 되었다. 해당 메소드들이 JPA의 표준 네이밍과 다른 점도 영향을 미쳤을 수 있다.
Spring @Transactional Annotation
@Transactional
어노테이션을 붙여주었다.// JpaPostRepository.java
private final EntityManager em;
public JpaPostRepository(EntityManager em) {
super();
this.em = em;
}
@Override
@Transactional
public Post save(Post post) {
// id, 생성일, 수정일 넣고 저장
post.setPOST_DATE(LocalDateTime.now());
post.setPOST_UPDATE(post.getPOST_DATE());
em.persist(post);
return post;
}
@Override
@Transactional
public Post update(Post post) {
post.setPOST_UPDATE(LocalDateTime.now());
em.merge(post);
return post;
}
@Override
@Transactional
public void delete(Long postId) {
em.createQuery("delete from Post p where p.POST_ID = :postId")
.setParameter("postId", postId)
.executeUpdate();
}
java.lang.IllegalArgumentException: id to load is required for loading
...
at com.ticktack.homey.repository.attach.JpaAttachRepository.findById(JpaAttachRepository.java:26) ~[main/:na]
JpaAttachRepository
파일에서 첨부파일 id로 첨부파일 객체를 찾을 때 첨부파일 id가 null인 경우를 가정하지 않아 생긴 오류// 첨부파일 정보 가져오기
// 기존 코드 : 첨부파일 id를 받는 attachRepository.findById가 무조건 실행됨
Optional<Attach> attach = attachRepository.findById(form.getATTF_ID());
attach.ifPresent(f -> {
form.setATTF_OBJ(f);
});
// 수정 코드 : id부터 없을 수 있다고 가정
Optional<Long> attfId = Optional.ofNullable(form.getATTF_ID());
attfId.ifPresent(id -> {
form.setATTF_OBJ(attachRepository.findById(id).get());
});
org.hibernate.QueryException: could not resolve property: postId of: com.ticktack.homey.domain.Post
...
at com.sun.proxy.$Proxy92.createQuery(Unknown Source) ~[na:na]
at com.ticktack.homey.repository.post.JpaPostRepository.delete(JpaPostRepository.java:52) ~[main/:na]
:
뒤에 오는 이름이 setParameter로 전달한 이름이어야 하고, where 바로 뒤는 DB 컬럼명이 와야 하는데 반대로 작성한 것을 발견해서 수정하였다.// 작동하는 코드
@Override
@Transactional
public void delete(Long postId) {
em.createQuery("delete from Post p where p.POST_ID = :postId")
.setParameter("postId", postId)
.executeUpdate();
}
antlr.NoViableAltException: unexpected token: from
...
at com.sun.proxy.$Proxy92.createQuery(Unknown Source) ~[na:na]
at com.ticktack.homey.repository.attach.JpaAttachRepository.delete(JpaAttachRepository.java:35) ~[main/:na]
delete from Attach~
가 되어야 하는데 delete a from Attach~
로 잘못 작성함. 기본적인 sql인데 헷갈린듯..ㅠㅠ// 작동하는 코드
@Override
@Transactional
public void delete(Long attachId) {
em.createQuery("delete from Attach a where a.ATTF_ID = :attachId")
.setParameter("attachId", attachId)
.executeUpdate();
}