Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Quartz job throwing InvalidDataAccessApiUsageException: no transaction is in progress;

I have a very simple quartz job that is trying to fetch printer record from the DB,
I am getting this error:

org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; 
nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
javax.persistence.TransactionRequiredException: no transaction is in progress

Here is the code of the application:

@SpringBootApplication
@ComponentScan(basePackages="com.xerox.printhub") 
@EntityScan("com.xerox.printhub*")
@EnableTransactionManagement
@EnableJpaRepositories(basePackages ={"com.xerox.printhub.repository"})
@Import({ SchedulerConfig.class })
public class PrinterVerificationApp extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(PrinterVerificationApp.class, args);
    }
}

Here is the code of the Quartz job:

package com.xerox.printhub.quartz.jobs;

import javax.transaction.Transactional;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;



public class PrinterVerificationJob implements Job{

    @Transactional
    public void execute(JobExecutionContext jobExecutionContext) {

    private static final Logger logger = LoggerFactory.getLogger(PrinterVerificationJob.class);

    @Autowired
    Gson g;

       @Autowired
       PrinterInfoRepository printerRepo;   

        logger.debug("Trying to fetch an available printer...");

        PrinterInfo printerInfo=null;
        try {
            printerInfo = printerRepo.findOneAndLock(PrinterStatus.NEW.name());
            } catch (Exception e1) {
            logger.debug("Error!",e1);
            }

    }

Code of the Dao object (PrinterInfoRepository )

@Transactional
public interface PrinterInfoRepository  extends CrudRepository<PrinterInfo ,String> {

     @Lock(LockModeType.PESSIMISTIC_WRITE)//locking it to prevent other workers to access it.
     @Query("SELECT p FROM PrinterInfo p where p.statusCd = :status")
     List<PrinterInfo> findOneAndLock(@Param("status")String status);   
}

In my application, this Rest controller is working just fine with PrinterInfoRepository

package com.xerox.printhub.controllers;

import java.text.ParseException;
import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class PrinterAPIController {

    @Autowired 
    PrinterInfoRepository printerRepo;


    @PostMapping(value = "/SubmitPrinterData")
    public PrinterInfo submitPrinterData(
            @RequestBody PrinterInfoJason printerInfo) {
            //THIS METHOD IS WORKING FINE.
    }

I tried both annotations:

import javax.transaction.Transactional;
import org.springframework.transaction.annotation.Transactional;

And I tried annotating the PrinterVerificationJob with @service
I also added EnableTransactionManagement

But nothing helps.
Any idea what I'm doing wrong?

UPDATE

I found the culprit at PrinterInfoRepository-> findOneAndLock()
when I don't use LockModeType.PESSIMISTIC_WRITE, method works just find... when I try to access using use LockModeType.PESSIMISTIC_WRITE, I get the InvalidDataAccessApiUsageException What am I doing wrong? I need to lock the record from other workers.

 @Lock(LockModeType.PESSIMISTIC_WRITE)//locking it to prevent other workers to access it.
 @Query("SELECT p FROM PrinterInfo p where p.statusCd = :status")
 PrinterInfo findOneAndLock(@Param("status")String status);

full stack trace:

org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress 
        at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:402) 
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255) 
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527) 
        at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) 
        at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) 
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:139) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) 
        at com.sun.proxy.$Proxy124.findOneAndLock(Unknown Source) 
        at com.xerox.printhub.quartz.jobs.PrinterVerificationJob.getPrinter(PrinterVerificationJob.java:158) 
        at com.xerox.printhub.quartz.jobs.PrinterVerificationJob.execute(PrinterVerificationJob.java:189) 
        at org.quartz.core.JobRunShell.run(JobRunShell.java:202) 
        at org.quartz.simpl.SimpleThrexeroxool$WorkerThread.run(SimpleThrexeroxool.java:573) 
    Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress 
        at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1556) 
        at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1529) 
        at org.hibernate.query.Query.getResultList(Query.java:168) 
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
        at java.lang.reflect.Method.invoke(Method.java:498) 
        at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:402) 
        at com.sun.proxy.$Proxy141.getResultList(Unknown Source) 
        at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:129) 
        at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:91) 
        at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:136) 
        at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125) 
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) 
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) 
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) 
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) 
        ... 13 more
like image 341
JavaSheriff Avatar asked Apr 29 '19 21:04

JavaSheriff


1 Answers

From you log it is obvious - Why ? Because if the Transaction was active you would have had a Proxy invocation in between like when you invoke a method on the repository. Visible from the same log.

   at com.xerox.printhub.quartz.jobs.PrinterVerificationJob.getPrinter(PrinterVerificationJob.java:158)

    at com.xerox.printhub.quartz.jobs.PrinterVerificationJob.execute(PrinterVerificationJob.java:189)

    at org.quartz.core.JobRunShell.run(JobRunShell.java:202)

That your PrinterVerificationJob is not instrumented. Which means that your @Transactional annotation is not working. You need your Bean to be managed by spring for the @Transactional annotation to take effect. What you can do si to isolate the contents of your execute method into a service and inject it into the JOB. Then you can annotate this Class or the method of this class with @Transctional. This bean will get instrumented and the @Transactional annotation will take effect.

@Service
PrinterVerificationService {

@Transactional
public void verifyPrinter(yuor parameters here)

}

public class PrinterVerificationJob implements Job{
@Autowire
PrinterVerificationService verificationService;

public void execute(JobExecutionContext jobExecutionContext) {
    service.verifyPrinter(your prameters here)
}

If you would like to preserve your application logic as is. What you can do is to use the @PlatformTransactionManager that is already initialized in spring and @Autowire it to your job. With this transaction manager you can manualy start the transaction. With it you can initialize TransactionTemplate and do:

TransactionTemplate txTemplate = new TransactionTemplate(platformTransactionManagerHere);                
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallback<Object>() {
    public Object doInTransaction(TransactionStatus status) {
        // do stuff
    }
});
like image 69
Alexander Petrov Avatar answered Sep 29 '22 02:09

Alexander Petrov