TransactionRequiredException:没有事务正在进行春季批处理作业和休眠

Posted

技术标签:

【中文标题】TransactionRequiredException:没有事务正在进行春季批处理作业和休眠【英文标题】:TransactionRequiredException: no transaction is in progress with sping batch job and hibernate 【发布时间】:2020-09-10 03:17:43 【问题描述】:

我正在使用 hibernate 学习 Spring 批处理作业,但我遇到了一个问题。

TransactionRequiredException:没有正在进行的事务

我已经创建了阅读器、处理器和编写器。我正在更新处理器中的用户,并且在编写器之后我收到此错误。我已经尝试在处理器上使用 @Transacional 方法,但它不起作用。我不确定这里有什么问题。添加我的作业配置文件。在此之前,我面临与事务管理器相关的问题。我也不确定我使用的事务管理器是否合适。请让我知道我在哪里犯了错误。

import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean; 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class JobConfiguration 

@Bean
public DataSource jobDataSource() 
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/TestDB");
    dataSource.setUsername("****");
    dataSource.setPassword("****");
    return dataSource;


private JobRepository getJobRepository() throws Exception 
    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(jobDataSource());
    factory.setTransactionManager(getTransactionManager());
    factory.afterPropertiesSet();
    return (JobRepository) factory.getObject();


private PlatformTransactionManager getTransactionManager() 
    HibernateTransactionManager txManager = new HibernateTransactionManager();
    txManager.setSessionFactory(jobSessionFactory().getObject());
    return txManager;


@Bean
public JobLauncher myJobLauncher() throws Exception 
    SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
    jobLauncher.setJobRepository(getJobRepository());
    jobLauncher.afterPropertiesSet();
    return jobLauncher;


@Bean
public LocalSessionFactoryBean jobSessionFactory() 
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(jobDataSource());
    sessionFactory.setPackagesToScan("com.test");
    sessionFactory.setHibernateProperties(jobHibernateProperties());
    return sessionFactory;


@Bean
public Properties jobHibernateProperties() 
    Properties hibernateProperties = new Properties();
    hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
    hibernateProperties.put("hibernate.show_sql", false);
    hibernateProperties.put("hibernate.hbm2ddl.auto", "update");
    hibernateProperties.put("hibernate.format_sql", true);
    return hibernateProperties;


TestJob.Java

import org.hibernate.SessionFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import 
org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import 
org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.item.database.HibernateCursorItemReader;
import org.springframework.batch.item.database.builder.HibernateCursorItemReaderBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.cadau.jobs.dto.TestDTO;
import com.Test.model.User;

@Configuration
public class TestJob 

@Autowired
private SessionFactory sessionFactory;

@Autowired
private JobBuilderFactory jobBuilderFactory;

@Autowired
private StepBuilderFactory steps;

@Bean(name = "firstBatchJob")
public Job job(@Qualifier("step1")Step step1) 
    return jobBuilderFactory.get("firstBatchJob")
            .start(step1)
            .build();


@Bean(name="step1")
protected Step step1(ItemReader<User> itemReader,
  ItemProcessor<User, User> testProcessor,
  ItemWriter<User> testWriter) 
    return steps.get("step1").<User, User> chunk(10)
      .reader(itemReader)
      .processor(testProcessor)
      .writer(testWriter)
      .build();


@Bean(value = "itemReader")
public ItemReader itemReader() throws UnexpectedInputException, ParseException 
    HibernateCursorItemReader<User> reader = new HibernateCursorItemReader<>();
    reader.setQueryString("from User");
    reader.setFetchSize(1000);
    reader.setSessionFactory(sessionFactory);
    reader.setUseStatelessSession(true);
    return reader;


TestProcessor.java

@Transactional
@Component(value = "testProcessor")
@StepScope
public class TestProcessor implements ItemProcessor<User, User> 

@Autowired
private IUserService userService;

@Override
public User process(User item) throws Exception 
    System.out.println("Processor:::  "+item);
    User user = userService.edit(item.getId());
    System.out.println("DB user : "+user);
    user.setFullName("Test User");
    userService.update(user);
    System.out.println("After update user" + user);
    return item;


Testwriter.java

@StepScope
@Component(value = "testWriter")
public class TestWriter implements ItemWriter<User>

@Override
public void write(List<? extends User> items) throws Exception 
    System.out.println("Writer called.");


也添加错误

2020-05-23 14:59:05,098 INFO  [restartedMain] org.springframework.batch.core.launch.support.SimpleJobLauncher$1: Job: [SimpleJob: [name=firstBatchJob]] launched with the following parameters: []
2020-05-23 14:59:06,679 INFO  [restartedMain] org.springframework.batch.core.job.SimpleStepHandler: Executing step: [step1]
2020-05-23 14:59:14,965 INFO  [restartedMain] org.hibernate.hql.internal.QueryTranslatorFactoryInitiator: HHH000397: Using ASTQueryTranslatorFactory
Processor:::  com.cadau.model.User@3eae26f1
DB user : com.cadau.model.User@3edb84f6
After update usercom.cadau.model.User@3edb84f6
Processor:::  com.cadau.model.User@3b237ad2
DB user : com.cadau.model.User@2c527a91
After update usercom.cadau.model.User@2c527a91
Writer called.
2020-05-23 14:59:24,011 INFO  [restartedMain] org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback: Commit failed while step execution data was already updated. Reverting to old version.
2020-05-23 14:59:24,018 ERROR [restartedMain] org.springframework.batch.core.step.AbstractStep: Encountered an error executing step step1 in job firstBatchJob
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:398)
at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3558)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1444)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1440)
at org.springframework.orm.hibernate5.SessionFactoryUtils.flush(SessionFactoryUtils.java:147)
at org.springframework.orm.hibernate5.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:95)
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:96)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:922)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy126.commit(Unknown Source)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:152)
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:203)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:399)
at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:313)
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:144)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:137)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127)
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.run(Unknown Source)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:207)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:181)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:168)
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:163)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:764)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:319)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203)
at com.cadau.jobs.app.CadauJobsApplication.main(CadauJobsApplication.java:27)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
2020-05-23 14:59:24,432 INFO  [restartedMain] org.springframework.batch.core.launch.support.SimpleJobLauncher$1: Job: [SimpleJob: [name=firstBatchJob]] completed with the following parameters: [] and the following status: [FAILED]

【问题讨论】:

你从哪里得到异常?请张贴代码 @SimonMartinelli。我添加了代码和我得到的异常。执行作家后,我遇到了异常。你可以在日志中看到。它正在打印我的编写器代码,然后出现错误。可能是我在提交事务时收到此错误。但不确定 【参考方案1】:

由于您使用的是 Hibernate,因此您需要将 Spring Batch 配置为使用 HibernateTransactionManager 来驱动事务。作为mentioned in the reference documentation,方法是定义BatchConfigurer 并覆盖getTransactionManager()。但是,这不是您的代码所做的:

private PlatformTransactionManager getTransactionManager() 
   HibernateTransactionManager txManager = new HibernateTransactionManager();
   txManager.setSessionFactory(jobSessionFactory().getObject());
   return txManager;

你可以让JobConfiguration扩展DefaultBatchConfigurer

@Configuration
public class JobConfiguration extends DefaultBatchConfigurer 

   @Override
   public HibernateTransactionManager getTransactionManager() 
      HibernateTransactionManager txManager = new HibernateTransactionManager();
      txManager.setSessionFactory(jobSessionFactory().getObject());
      return txManager;
   


如果您使用@EnableBatchProcessing,这也是执行此操作的方法,请参阅以“为了使用自定义事务管理器,应提供自定义 BatchConfigurer。例如:”开头的部分中的Javadoc。 .

【讨论】:

【参考方案2】:

首先,您必须将项目保存在编写器中,而不是处理器中。处理器用于可选转换,同时项目从读取器传递到写入器。您不需要处理器上的@Transactional。事务是默认配置的。事务管理器可以选择在 StepBuilderFactory steps.get("step1").transactionManager(transactionManager) 中配置,但这通常是默认完成的。只需将 transactionManager 声明为 bean。

最后,您可以使用 @EnableBatchProcessing 并完全删除 JobConfiguration。

【讨论】:

嗨,Rasha Elsayed 感谢您的回复。但是,如果我将 transactionManager 声明为 bean,则会出现以下错误。 org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有可用的“org.springframework.transaction.PlatformTransactionManager”类型的合格bean:预期的单个匹配bean但找到了2:getTransactionManager,transactionManager我已经提到了这个站点:baeldung.com/introduction-to-spring-batch 您是否尝试将 @Transactional 注释以及 userService.update 从处理器删除到编写器?顺便说一句,您不需要自定义编写器,您可以配置 JpaItemWriter 或 JdbcBatchItemWriter

以上是关于TransactionRequiredException:没有事务正在进行春季批处理作业和休眠的主要内容,如果未能解决你的问题,请参考以下文章