@PostConstruct와 no EntityManager

2022. 5. 31. 13:32ERROR

에러 메시지
Caused by: javax.persistence.TransactionRequiredException: No EntityManager with actual 
transaction available for current thread - cannot reliably process 'persist' call

웹 개발중 테스트 데이터를 매 실행마다 입력하기 번거로워서 

jpa:
  hibernate:
    ddl-auto: create

update로 변경할까 하다가, 그냥 애플리케이션이 실행할때마다 샘플 데이터를 저장하기로 했다.

일단 트랜잭션이 걸려있는 Service 단에서 sampleData 메소드를 작성했다.
빈 생성(여기서는 해당 코드가 있는 Service 클래스)과 의존관계 주입이 완료된 다음, sampleData 메소드가 실행되어
book 2개가 저장될거라고 기대했는데 맨 위의 오류코드를 맞닥뜨렸다.

현재 쓰레드에서 트랜잭션을 얻은 EntityManager가 없다는 뜻으로 해석된다. 

구글링으로 도움을 얻은 링크를 첨부한다.


https://reflectoring.io/spring-boot-execute-on-startup/#postconstruct

The @PostConstruct method is called right after the bean has been created by Spring
A @PostConstruct method is inherently tied to a specific Spring bean so it should be used for the initialization logic of this single bean only.

 

https://stackoverflow.com/questions/27570641/no-transactional-entitymanager-available-in-postconstruct

In the @PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions.

 

@PostConstruct 로 EntityManager를 사용 단일 Bean의 초기화 로직에만 사용해야 한다. @PostConstruct는 해당 빈만 생성되었다고 판단한 다음 호출되기 때문이다. 이는 곧, 해당 빈에 관련된 AOP 등을 포함하여, 전체 스프링 애플리케이션 컨텍스트가 초기화되었다는 것을 보장하지 않는다.

따라서 @PostConstruct 에서 사용가능한 트랜잭션이 없다는 말도 타당하다. 트랜잭션 AOP와 @PostConstruct의 적용 시점이 다르기 때문이다. AOP는 후 처리(post processing)가 완료되고 스프링의 애플리케이션 컨텍스트가 초기화 된 이후가 동작한다.

아래와 같은 해결방법을 찾았다.

https://sorjfkrh5078.tistory.com/311

  • 다른 스프링 빈을 호출해서 사용하는 방법
  • AOP를 사용하지 않고 트랜잭션을 직접 코딩하는 방법
  • 애플리케이션 컨텍스트가 완전히 초기화된 이벤트를 받아서 호출하는 방법
  • 초기화하는 메서드와 초기화를 실행하는 메서드를 분리하는 방법

세번째 방법으로 해결했다. 

@EventListener(ApplicationReadyEvent.class)

위의 어노테이션을 추가한 것인데, 해당 메소드는 애플리케이션의 구동이 완료된 후에 실행된다. 찾고 있던 바로 그 기능이다. 

아래는 출력한 로그다.  @PostConstruct 로그와 ApplicationReadyEvent의 로그가 서로 다른 시점에 찍힌다.

 

마지막 방법 "초기화하는 메서드와 초기화를 실행하는 메서드를 분리하는 방법"은 아래의 블로그에서 확인할 수 있다.
https://velog.io/@hungry_foolish/Spring-PostConstruct%EC%97%90%EC%84%9C-transactional-%EC%B2%98%EB%A6%AC

 

Spring의 EventListener는 추후에 공부해야겠다.