【发布时间】:2014-09-03 08:24:31
【问题描述】:
我在 Spring Batch tasklet 中使用 Spring Data Jpa 存储库时遇到问题。
我希望使用为此步骤配置的myTransactionManager 在MyOrderTasklet's execute 方法中有一个有效的休眠事务。但是一旦调用了flush() 或者执行方法被留下(没有显式调用flush())。我得到一个
TransactionRequiredException"no transaction is in progress".
调试时,我看到一个事务在进入 tasklet 的执行方法之前由 Spring Batch 创建,并且通过在 org.springframework.orm.jpa.JpaTransactionManager#doBegin() 中调用 getJpaDialect().beginTransaction() 创建了一个有效的 hibernateTransaction 并将其放入 org.hibernate.jdbc.JDBCContext 实例中。
当调用orderRepository 的方法时,我看到对AbstractPlatformTransactionManager#getTransaction 的调用找到了现有事务并调用handleExistingTransaction。但后来 org.hibernate.ejb.AbstractEntityManagerImpl#isTransactionInProgress 返回 false 因为找不到 hibernateSession。
我看到不同的 Hibernate 会话和 EntityManagers 是为围绕 tasklet 的执行方法的事务和对存储库的调用而创建的。内部休眠会话找不到绑定到外部休眠会话的外部休眠事务。
任何想法如何解决这个问题?相同的休眠会话如何用于 tasklet 的执行方法和对存储库的调用? 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/:?]
【问题讨论】:
标签: java spring hibernate jpa transactions