Spring Data JPA - javax.persistence.TransactionRequiredException: 执行更新/删除查询

5

我正在尝试使用Spring CRUDRepository Class更新表格(@query注释),但系统在更新时抛出以下错误。我已经搜索过并找到了许多帖子,告知添加transactional注释。我尝试过了但没有效果。请问有人能帮助我吗?

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;

    @Repository
    public interface PrintJobItemRepo extends CrudRepository<PrintJobItem, Integer> {

        @Modifying     
        @Query(value =  PrintBatchConstants.UPDATE_SEQUENCE_NUMBER_QUERY)
        public void updateSequenceNumberInPrintItemTable(String sequenceNumber, String groupId);    
    }

    public class PrintBatchConstants {

        public static final String UPDATE_SEQUENCE_NUMBER_QUERY="UPDATE PRINT_JOB_ITEM SET PRINT_STATUS=null,SEQUENCE_NBR=(?1) "
                                                                 + "where GROUP_ID=(?2) and sequence_nbr is null";

    }

以下是错误信息

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:403) ~[spring-orm-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:257) ~[spring-orm-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) ~[spring-orm-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:149) ~[spring-data-jpa-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at com.sun.proxy.$Proxy83.updateSequenceNumberInPrintItemTable(Unknown Source) ~[na:na]
    at com.acm.bap.printbatch.tasklet.UpdateSequenceNumbers.updateSequenceNumbers(UpdateSequenceNumbers.java:100) ~[classes/:na]
    at com.acm.bap.printbatch.tasklet.UpdateSequenceNumbers.execute(UpdateSequenceNumbers.java:56) ~[classes/:na]
    at com.acm.bap.printbatch.tasklet.UpdateSequenceNumbers$$FastClassBySpringCGLIB$$afc7f92b.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:687) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at com.acm.bap.printbatch.tasklet.UpdateSequenceNumbers$$EnhancerBySpringCGLIB$$88a0f8bb.execute(<generated>) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_162]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_162]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_162]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_162]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:136) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at com.sun.proxy.$Proxy67.execute(Unknown Source) ~[na:na]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:68) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:68) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:137) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:319) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:147) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) [spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
    at com.acm.bap.printbatch.AgencyBillPayFileUploadApplication.run(AgencyBillPayFileUploadApplication.java:51) [classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:784) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:768) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) [spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at com.acm.bap.printbatch.AgencyBillPayFileUploadApplication.main(AgencyBillPayFileUploadApplication.java:39) [classes/:na]
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1605) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution$ModifyingExecution.doExecute(JpaQueryExecution.java:238) ~[spring-data-jpa-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:88) ~[spring-data-jpa-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:154) ~[spring-data-jpa-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:142) ~[spring-data-jpa-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:618) ~[spring-data-commons-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:605) ~[spring-data-commons-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) ~[spring-data-commons-2.2.6.RELEASE.jar:2.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    ... 50 common frames omitted

我根据评论更新了消费类

    public class UpdateSequenceNumbers extends PrintBatchConstants implements Tasklet { 

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

        @Autowired
        PrintJobItemRepo pjr;

        @Override
        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

            ArrayList<String> grpIds=(ArrayList)chunkContext.getStepContext().getJobExecutionContext().get(PrintBatchConstants.batchGroupIds);

            String groupIds = BatchCommonUtilities.getStringFromList((List<String>) chunkContext.getStepContext().getJobExecutionContext().get(PrintBatchConstants.batchGroupIds));

            List<String> flatRecords = pjr.findFlatGroupIdsIn(grpIds);
            List<String> foldRecords = pjr.findFoldGroupIdsIn(grpIds);

            logger.info("Flat SIZE "+flatRecords.size());
            logger.info("Fold SIZE "+ foldRecords.size());

           this.updateSequenceNumbers(flatRecords);
            this.updateSequenceNumbers(foldRecords);

            return null;
        }
        @Transactional
        public void updateSequenceNumbers(List<String> groupIds) {

            logger.info("Enter -> updateSequenceNumber");

            int groupNosSize=groupIds.size();

            StringBuilder sequenceNbr=null;

            for (int i = 0; i < groupIds.size(); i++) {
                    sequenceNbr=new StringBuilder();
                    sequenceNbr.append(i+1);
                    sequenceNbr.append(" Of ");
                    sequenceNbr.append(groupNosSize);
                    pjr.updateSequenceNumberInPrintItemTable(sequenceNbr.toString(),groupIds.get(i));
            }
            logger.info("Completed -> updateSequenceNumber");
        }

入口点类

@SpringBootApplication
@ComponentScan(basePackages = "com.acm.bap.printbatch")
@EnableTransactionManagement
@ImportResource({ "BAPBatchInfrastructure.xml", "DownstreamAppConfig.xml" })
public class DownstreamFileUploadApplication extends PrintBatchConstants implements CommandLineRunner {

    @Autowired
    private NotifyConfig notify;

    @Autowired
    private ApplicationContext ctx;

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

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(DownstreamFileUploadApplication.class);

        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);

    }

    @Override
    public void run(String... args) throws Exception {

        logger.info(notify.getEnvironment());

        JobLauncher jobLauncher = ctx.getBean(JobLauncher.class);
        Job job = ctx.getBean(Job.class);

        jobLauncher.run(job,
                new JobParametersBuilder()
                .addString(batchDocumentClass, "InvoiceStatementDocumentation")
                .addString(batchType, "2020-01-27")
                .addString(batchEmailID, notify.getSupportEmailId())
                .addString(batchEnvironment, notify.getEnvironment())
                .toJobParameters());



    }

}

我认为你需要在类上加上注释“EndUserTransaction”。另外,还有其他变体,请参阅文档。 - Adrian M.
嗨,Adrian, 我在文档中找不到这样的注释。我认为我已经添加了所需的注释,但还有其他缺失的地方。 - Saimuga
你关于注释的说法是正确的,我很抱歉。那是一个自定义注释...哎呀。 - Adrian M.
请问能分享一下消费类吗? - Adrian M.
嗨,Adrian,我已经添加了消费类。请检查。 - Saimuga
谢谢,你尝试给这个类添加了 @Transactional 吗? - Adrian M.
4个回答

4

尝试在PrintJobItemRepo上应用@Transactional而不是在方法级别应用它

    @Repository
    @Transactional
    public interface PrintJobItemRepo extends CrudRepository<PrintJobItem, Integer> 

2
@abhinav kumar的答案对我有用,你只需要在你的接口中加入@Transactional注释即可。
@Repository
@Transactional
public interface PrintJobItemRepo extends CrudRepository<PrintJobItem, Integer> 

2
哪个 @Transactional?javax.transaction 还是 org.springframework.transaction.annotation? - Christoph
1
重复已经由@abhinav kumar提供的相同答案有什么意义呢? - Somebody

1

@Transactional注解作为Spring的一个方面(aspect)工作,这意味着Spring会创建一个带有注解方法的类的代理(proxy),因此在运行时对该方法的调用将被重定向到代理方法,代理方法将真实方法的调用包装在一个事务中。

在您的代码中,请从存储库中删除@Transactional注解:

public interface PrintJobItemRepo extends CrudRepository<PrintJobItem, Integer> {

    @Modifying     
    @Query(value =  PrintBatchConstants.UPDATE_SEQUENCE_NUMBER_QUERY)
    public void updateSequenceNumberInPrintItemTable(String sequenceNumber, String groupId);
}

我移除了 @EnableTransactionManagement,将其放在带有 @Configuration 注解的 bean 中或与 @SpringBootApplication 相同的类中。
接下来,在进行更新调用的方法中添加 @Transactional。
@Transactional
public void updateSequenceNumbers(List<String> groupIds) {

    logger.info("Enter -> updateSequenceNumber");

    int groupNosSize=groupIds.size();

    StringBuilder sequenceNbr=null;

    for (int i = 0; i < groupIds.size(); i++) {
            sequenceNbr=new StringBuilder();
            sequenceNbr.append(i+1);
            sequenceNbr.append(" Of ");
            sequenceNbr.append(groupNosSize);


            pjr.updateSequenceNumberInPrintItemTable(sequenceNbr.toString(),groupIds.get(i));
    }

    logger.info("Completed -> updateSequenceNumber");

}

让我知道它是否有效。


请提供完整的异常堆栈跟踪。 - CarlosJavier
嗨,Carlos Javier,我已经添加了完整的异常堆栈追踪。请检查。 - Saimuga
你好,CarlosJavier,我已经在我的jar入口类中添加了@EnableTransactionManagement,在这里SpringBoot应用程序得到初始化。 - Saimuga
嗨,CarlosJavier,我已经更新了我的原始帖子,加入了入口类。 - Saimuga
嗨,CarlosJavier,当然,谢谢你的所有建议。我会浏览链接并告诉你是否发现了什么。 - Saimuga
显示剩余3条评论

0
在我的情况下,将 @Transactional 方法放入 Spring 的上下文中,而不是放在外面。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接