문제원인
빈 생명주기 콜백 메서드를 사용해서 더미 데이터를 DB에 넣으려고 하니 에러가 뜬다.
no entitymanager with actual transaction available for current thread
구글링을 해보니 @PostConstruct에는 직접적으로 @Transactional을 적용할 수 없다고 한다.
그래서 인프런 커뮤니티에 달린 글을 찾아봤는데 찾은 내용이 다음과 같다.
@PostConstruct는 해당 빈 자체만 생성되었다고 가정하고 호출됩니다. 해당 빈에 관련된 AOP등을 포함한, 전체 스프링 애플리케이션 컨텍스트가 초기화 된 것을 의미하지는 않습니다.
트랜잭션을 처리하는 AOP등은 스프링의 후 처리기(post processer)가 완전히 동작을 끝내서, 스프링 애플리케이션 컨텍스트의 초기화가 완료되어야 적용됩니다.
정리하면 @PostConstruct는 해당빈의 AOP 적용을 보장하지 않습니다.
이런 것을 우회하는 여러가지 방법이있는데요. 제가 보여드린 방법(다른 스프링 빈을 호출해서 사용하는 방법)을 포함해서, AOP를 사용하지 않고 트랜잭션을 직접 코딩하는 방법, 애플리케이션 컨텍스트가 완전히 초기화 된 이벤트를 받아서 호출하는 방법 등이 있습니다.
관련해서 다음 링크를 참고해보시면 더욱 자세한 도움을 받을 수 있습니다^^
https://stackoverflow.com/questions/17346679/transactional-on-postconstruct-method
- 김영한 선생님 (출처:https://www.inflearn.com/questions/26902)
해결
@PostConstruct는 해당빈의 AOP 적용을 보장하지 않으므로 우회해야한다.
해당 빈에 inner static 클래스를 생성해서 @Transactional를 붙인 메서드를 init 메서드에서 호출해야하는 방법은 이 포스팅을 참고,
나는 애플리케이션 컨텍스트가 완전히 초기화 된 이벤트를 받아서 호출하는 방법을 사용했다 : 참고한 포스팅
@Component
@RequiredArgsConstructor
public class TestDataInit {
private final H2MemberRepository memberRepository;
@EventListener(ApplicationReadyEvent.class)
@Transactional
public void initDB(){
initTestMember();
}
public void initTestMember() {
Member member1 = new Member("testid1","testpass1","테스트사용자1");
Member member2 = new Member("testid2","testpass2","테스트사용자2");
memberRepository.save(member1);
memberRepository.save(member2);
}
}
잘 적용된다.
'Web > Spring' 카테고리의 다른 글
DTO의 사용범위는 어디까지? 또, DTO 변환은 어디서? (4) | 2022.09.24 |
---|---|
[Error] Update/delete queries cannot be typed (0) | 2022.09.06 |
검증 - 직접구현부터 Spring Validation 까지 (0) | 2022.08.05 |
spring-boot-devtools (0) | 2022.08.04 |
MapStruct 적용 (0) | 2022.06.08 |