我在 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/