2014-09-03 156 views
4

我有问题在Spring批处理任务内使用Spring Data Jpa仓库。 我期望在MyOrderTasklet'sexecute方法中使用为此步骤配置的myTransactionManager进行有效的休眠事务。但只要flush()被调用,或者执行方法被留下(没有明确地调用flush())。我得到一个春天的数据Jpa仓库在春天批处理tasklet抛出TransactionRequiredException

TransactionRequiredException"no transaction is in progress". 

调试时我看到一个事务都通过弹簧一批进入微进程的执行方法之前和有效hibernateTransaction创建并通过调用org.springframework.orm.jpa.JpaTransactionManager#doBegin()getJpaDialect().beginTransaction()放在org.hibernate.jdbc.JDBCContext实例创建的。

当调用orderRepository的方法时,我看到对AbstractPlatformTransactionManager#getTransaction的调用找到了现有的事务并调用handleExistingTransaction。但后来org.hibernate.ejb.AbstractEntityManagerImpl#isTransactionInProgress返回false,因为没有找到hibernateSession。

我看到不同的Hibernate会话和EntityManagers被创建用于围绕tasklet的execute方法和对资源库调用的事务。内部休眠会话无法找到绑定到外部休眠会话的外部休眠事务。

任何想法如何解决这个问题?如何将相同的hibernate会话用于tasklet的execute方法和对资源库的调用?可以将hibernateTemplate以某种方式传播到其他会话?

下面是一些代码提取物,显示我的设置:

@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; 
    } 
} 

例外:

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/:?] 

回答

3

我怀疑下面的代码:

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

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

实体管理器通常被使用插入:

@PersistenceContext 
EntityManager entityManager; 

即使你没有申报这类豆,春天仍然会创建一个并会为您提供一个最适合你的当前运行的事务。

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

+0

我也看到了应用程序,只需执行EntityManager的@Autowire。但我认为弗拉德的其他评论:“即使你没有声明这样的bean,Spring仍然会创建一个,并且会为你提供一个适合你当前正在运行的事务的东西。“,我不确定这一点,我认为你需要在你的Spring配置文件中明确声明EntityManager。 – 2014-09-07 20:22:59

+0

也btw我总是喜欢定义像”hibernate.default_schema“或在春季定义的包扫描目录配置XML而不是在代码中,你可能想配置这些参数而不重新编译代码,所以我认为配置应该总是属于XML文件而不是应用代码 – 2014-09-07 20:25:38

+0

EntityManager与上下文相关,两个并发线程将使用一个不同的EntityManager实例,所以Spring必须为当前的事务生命周期创建这样的对象,并在事务结束时简单地抛弃它,如果你明确地创建它,它将如何与持久化上下文bean的上下文绑定特性相一致? – 2014-09-07 20:25:56

0

尽管我将pom.xml中的hibernate verison定义为4.3.5,但我们项目中的另一个库对hibernate 3.6.10有一个依赖关系,然后使用它(我没有注意到)。在摆脱Hibernate 3并使用最新的hibernate 4版本(目前4.3.5)后,错误消失了。