java - spring batch tasklet 中的 Spring Data Jpa 存储库抛出 TransactionRequiredException

标签 java spring hibernate jpa transactions

我在 Spring Batch tasklet 中使用 Spring Data Jpa 存储库时遇到问题。 我希望在 MyOrderTasklet's 中有一个有效的 hibernate 事务execute使用 myTransactionManager 的方法这是为此步骤配置的。但是一旦flush()被调用或者如果 execute 方法被保留(没有显式调用 flush() )。我得到一个

TransactionRequiredException"no transaction is in progress".

调试时,我看到在进入 tasklet 的执行方法之前由 spring batch 创建了一个事务,并且创建了一个有效的 hibernateTransaction 并将其放入 org.hibernate.jdbc.JDBCContext 中。通过调用实例 getJpaDialect().beginTransaction()org.springframework.orm.jpa.JpaTransactionManager#doBegin() .

orderRepository 的方法时被调用,我看到调用AbstractPlatformTransactionManager#getTransaction查找现有交易并调用 handleExistingTransaction .但后来 org.hibernate.ejb.AbstractEntityManagerImpl#isTransactionInProgress返回 false,因为找不到 hibernateSession。

我看到不同的 Hibernate session 和 EntityManagers是为围绕 tasklet 的执行方法的事务和对存储库的调用而创建的。内部 hibernate session 无法找到绑定(bind)到外部 hibernate session 的外部 hibernate 事务。

有什么解决办法吗?同一个 hibernate session 如何用于 tasklet 的执行方法和对存储库的调用?可以hibernateTemplate也许会以某种方式传播到其他 session ?

这里有一些代码摘录来展示我的设置:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackageClasses = MyRepositoryConfig.class)
@ComponentScan(basePackageClasses=MyRepositoryConfig.class)
public class MyRepositoryConfig {

    @Autowired
    private InfrastructureConfiguration infrastructureConfiguration;

    @Bean
    public EntityManagerFactory entityManagerFactory() throws SQLException {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(infrastructureConfiguration.hibernateJpaVendorAdapter());
        factory.setPackagesToScan("com.example.model");
        factory.setDataSource(infrastructureConfiguration.dataSource());
        if (StringUtils.hasText(infrastructureConfiguration.getSchema())) {
            factory.getJpaPropertyMap().put("hibernate.default_schema", infrastructureConfiguration.getSchema());
        }
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    @Bean(name= { "transactionManager", "myTransactionManager"})
    public PlatformTransactionManager transactionManager() throws SQLException {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory());
        return txManager;
    }

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }

    @Bean
    public MyRepositoryService myRepositoryService() {
        return new myRepositoryServiceImpl();
    }
}

@Configuration
public class DefaultInfrastructureConfiguration implements InfrastructureConfiguration {

    @Value("${my.schema:MYSCHEMA}")
    private String defaultSchema;

    @Value("${novis.jdbc.url:jdbc:oracle:thin:@//example.com/example}")
    private String jdbcUrl;

    @Value("${novis.jdbc.username:scott}")
    private String jdbcUsername;

    @Value("${novis.jdbc.password:tiger}")
    private String jdbcPassword;

    @Value("${novis.jdbc.driverClassName:oracle.jdbc.driver.OracleDriver}")
    private String jdbcDriverClassName;

    @Value("${novis.hibernate.database:ORACLE}")
    private String hibernateDatabase;

    @Bean
    @Override
    public DataSource dataSource() {
        BasicDataSource ds = new BasicDataSource();

        ds.setDriverClassName(jdbcDriverClassName);
        ds.setUrl(jdbcUrl);
        ds.setUsername(jdbcUsername);
        ds.setPassword(jdbcPassword);
        ds.setTestWhileIdle(true);
        ds.setValidationQuery("SELECT 1 FROM DUAL");

        return ds;
    }

    @Override
    public HibernateJpaVendorAdapter hibernateJpaVendorAdapter() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);
        vendorAdapter.setDatabase(Database.valueOf(Database.class, hibernateDatabase));
        return vendorAdapter;
    }

    @Override
    public String getSchema() {
        return defaultSchema;
    }
}

@Configuration
@Import(MyRepositoryConfig.class)
public class OrderManagerConfig {
    @Autowired
    @Qualifier("myTransactionManager")
    private PlatformTransactionManager myTransactionManager;

    ....        
    @Bean
    public Tasklet MyOrderTasklet() {
        return new MyOrderTasklet();
    }
    ....
    @Bean
    public Step processMyOrderErrorsStep() {
        return steps.get("processMyOrderErrorsStep").
            transactionManager(myTransactionManager).
            tasklet(myOrderProcessor()).
            listener(stepExecutionLoggerListener()).
            build();
    }
    ....
}

public class MyOrderTasklet implements Tasklet {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private PlatformTransactionManager myTransactionManager;

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

        for (Order order : orderRepository.findByErrorIsNotNullAndErrorHandledFalse()) {
            // Handle the error...
            order.setErrorHandled(true);
            orderRepository.saveAndFlush(order);
        }

        return RepeatStatus.FINISHED;
    }
}

异常(exception):

03 Sep 2014 09:53:39:902 ERROR AbstractStep:225 - Encountered an error executing step processMyOrderErrorsStep in job processMyOrderErrors
javax.persistence.TransactionRequiredException: no transaction is in progress
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:959) ~[hibernate-entitymanager-3.6.10.Final.jar:3.6.10.Final]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_51]
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:342) ~[spring-orm-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at com.sun.proxy.$Proxy153.flush(Unknown Source) ~[?:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_51]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:289) ~[spring-orm-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at com.sun.proxy.$Proxy153.flush(Unknown Source) ~[?:?]
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.flush(SimpleJpaRepository.java:436) ~[spring-data-jpa-1.6.1.RELEASE.jar:?]
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:404) ~[spring-data-jpa-1.6.1.RELEASE.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_51]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_51]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_51]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_51]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:405) ~[spring-data-commons-1.8.1.RELEASE.jar:?]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:390) ~[spring-data-commons-1.8.1.RELEASE.jar:?]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:344) ~[spring-data-commons-1.8.1.RELEASE.jar:?]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111) ~[spring-data-jpa-1.6.1.RELEASE.jar:?]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at com.sun.proxy.$Proxy198.saveAndFlush(Unknown Source) ~[?:?]
    at ch.local.ordermanager.MyOrderTasklet.execute(MyOrderTasklet.java:41) ~[classes/:?]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) ~[spring-tx-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:271) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:77) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:368) ~[spring-batch-infrastructure-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144) ~[spring-batch-infrastructure-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257) ~[spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:198) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:386) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:304) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at org.springframework.batch.core.launch.support.SimpleJobOperator.start(SimpleJobOperator.java:314) [spring-batch-core-3.0.1.RELEASE.jar:3.0.1.RELEASE]
    at ch.local.batchmanager.BatchJobRunner.runJob(BatchJobRunner.java:20) [classes/:?]
    at ch.local.batchmanager.BatchManager.run(BatchManager.java:30) [classes/:?]
    at ch.local.common.base.AbstractBatchApplication.execute(AbstractBatchApplication.java:38) [classes/:?]
    at ch.local.batchmanager.BatchManager.main(BatchManager.java:9) [classes/:?]

最佳答案

我怀疑是下面这段代码:

@Bean
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
    return entityManagerFactory.createEntityManager();
}

通常您不配置 entityManager 单例,让 Spring 决定何时在事务边界创建新实例。

通常使用以下方式插入实体管理器:

@PersistenceContext
EntityManager entityManager;

即使您没有声明这样的 bean,Spring 仍会创建一个并为您提供适合您当前正在运行的事务的那个。

使用单例 EntityManager 是有问题的,因为 EntityManager 不是线程安全的并且在不清除的情况下重复使用它也可能导致内存泄漏和陈旧的实体版本。

关于java - spring batch tasklet 中的 Spring Data Jpa 存储库抛出 TransactionRequiredException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25639600/

相关文章:

java - Storm-HBase Trident - 同时查询多个列

java - 如何使用 .equals() 以外的方法比较 java 中的列表

java - hibernate : How to use sql to retrieve a predefined object ?

java - Hibernate 进行不必要的子查询来获取数据

java - 访问堆栈顶部元素以递归比较两个堆栈元素是否相同的方法

java - 如何阻止 Proguard 删除返回类型?

java - spring项目中大量的数据库迁移

java - 如何以 Spring 形式显示值:input from arrayList?

java - Hibernate 在不同线程上运行时不必要地创建新实体

java - 使用 spring Data JPA 将 sql 查询的结果映射到 pojo