Trigger 발생 후 Job 의 execute 가 실행될 때
@Transactional
@Override
public void execute(JobExecutionContext context) throws JobExecutionException { //예약된 좌석이 결제 30분 후 아직 '입실 전' 상태 일 때 좌석 취소 시킴
Long bookingId = context.getJobDetail().getJobDataMap().getLong("bookingId");
Booking booking = bookingRepository.findByBookingId(bookingId);
if (booking != null && booking.getSeat() != null && booking.getStatus().equals(BookingStatus.beforeEnteringRoom)) {
booking.cancelBeforeEnteringSeat();
booking.getSeat().updateSeatStatus(true); // 좌석 사용 가능으로 변경
}
}
2024-07-23T04:52:17.049+09:00 ERROR 68 --- [QuartzThread--1] org.quartz.core.JobRunShell : Job cancelBeforeEnteringSeatGroup.cancelBeforeEnteringSeatJob_22 threw an unhandled Exception:
org.hibernate.LazyInitializationException: could not initialize proxy [com.example.catchStudyEx.domain.entity.Seat#22] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:314) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:44) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:102) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at com.example.catchStudyEx.domain.entity.Seat$HibernateProxy$KlEAiIgl.updateSeatStatus(Unknown Source) ~[main/:na]
at com.example.catchStudyEx.global.Job.CancelBeforeEnteringSeatJob.execute(CancelBeforeEnteringSeatJob.java:37) ~[main/:na]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar:na]
2024-07-23T04:52:17.053+09:00 ERROR 68 --- [QuartzThread--1] org.quartz.core.ErrorLogger : Job (cancelBeforeEnteringSeatGroup.cancelBeforeEnteringSeatJob_22 threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.2.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) ~[quartz-2.3.2.jar:na]
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [com.example.catchStudyEx.domain.entity.Seat#22] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:314) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:44) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:102) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at com.example.catchStudyEx.domain.entity.Seat$HibernateProxy$KlEAiIgl.updateSeatStatus(Unknown Source) ~[main/:na]
at com.example.catchStudyEx.global.Job.CancelBeforeEnteringSeatJob.execute(CancelBeforeEnteringSeatJob.java:37) ~[main/:na]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na]
... 1 common frames omitted
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [com.example.catchStudyEx.domain.entity.Seat#22] - no Session
LazyInitializationException
는 Hibernate에서 지연 로딩된 엔티티를 세션이 없는 상태에서 접근하려고 할 때 발생하고 Quartz 작업이 트랜잭션 컨텍스트 외부에서 실행되기 때문에 발생하므로 지연 로딩을 피하기 위해 필요한 데이터를 미리 가져와야 함@Transactioanl
@Override
public void execute(JobExecutionContext context) throws JobExecutionException { //예약된 좌석이 결제 30분 후 아직 '입실 전' 상태 일 때 좌석 취소 시킴
printInfo();
Long bookingId = context.getJobDetail().getJobDataMap().getLong("bookingId");
Booking booking = bookingRepository.findByBookingId(bookingId);
if (booking != null && booking.getSeat() != null && booking.getStatus().equals(BookingStatus.beforeEnteringRoom)) {
Seat seat = seatRepository.findBySeatId(booking.getSeat().getSeatId()).orElse(null);
booking.cancelBeforeEnteringSeat();
seat.updateSeatStatus(true); // 좌석 사용 가능으로 변경
}
}
private void printInfo(){
boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active={}",txActive );
}
findBySeatId
로 명시적으로 로드하여 가져옴LazyInitializationException
는 없어졌지만 tx active = false, @Transactional 적용이 안됨.
찾아보니 스프링부트 애플리케이션에서 분리되어 나간 작업 클래스 모듈에서는 @Transactional
을 일반적인 경우처럼 쉽게 사용할 수 없고 Quartz 스케줄러가 기본적으로 Spring의 트랜잭션 관리 기능과 완벽하게 통합되지 않기 때문에 생기는 문제
@Autowired
private TransactionTemplate transactionTemplate;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException { //예약된 좌석이 결제 30분 후 아직 '입실 전' 상태 일 때 좌석 취소 시킴
Long bookingId = context.getJobDetail().getJobDataMap().getLong("bookingId");
try{
transactionTemplate.execute(status -> {
Booking booking = bookingRepository.findByBookingId(bookingId);
if (booking != null && booking.getSeat() != null && booking.getStatus().equals(BookingStatus.beforeEnteringRoom)) {
Seat seat = seatRepository.findBySeatId(booking.getSeat().getSeatId()).orElse(null);
if(seat != null){
booking.cancelBeforeEnteringSeat();
seat.updateSeatStatus(true); // 좌석 사용 가능으로 변경
}
}
return null;
});
}catch (Exception e){
log.error("Job execution resulted in exception: ", e);
throw new JobExecutionException(e);
}
}
TransactionTemplate
사용하여 해결