我有一个Spring Batch(3.0.6)/ Spring Batch Admin(2.0快照)应用程序,我正在尝试使用Quartz(2.2.2)安排作业。使用hsqldb的批量管理配置(我们根本不关心这些数据)我们在运行Quartz作业时遇到多个事务更改错误(尽管Spring Batch作业已启动并完成正常)。
我收到以下错误:
[2016-05-02 16:28:16.005] boot - 48230 INFO [scheduler_Worker-2] --- MyJobBean: Launching myBatchJob
[2016-05-02 16:28:16.014] boot - 48230 WARN [scheduler_Worker-1] --- MyBatchJob: Job failed with Exception PreparedStatementCallback; SQL [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]; transaction rollback: serialization failure; nested exception is java.sql.SQLTransactionRollbackException: transaction rollback: serialization failure
java.sql.SQLTransactionRollbackException: transaction rollback: serialization failure
at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source)
at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:873)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:866)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:629)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:866)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:927)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:932)
at org.springframework.batch.core.repository.dao.JdbcJobInstanceDao.createJobInstance(JdbcJobInstanceDao.java:115)
at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:135)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.batch.core.repository.support.AbstractJobRepositoryFactoryBean$1.invoke(AbstractJobRepositoryFactoryBean.java:172)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy61.createJobExecution(Unknown Source)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:125)
at org.springframework.batch.core.launch.JobLauncher$run.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at com.app.MyBatchJob.performJob(MyBatchJob.groovy:41)
at com.app.MyBatchJob$performJob.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:114)
at com.app.MyJobBean.executeInternal(MyJobBean.groovy:36)
at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: org.hsqldb.HsqlException: transaction rollback: serialization failure
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.Session.executeCompiledStatement(Unknown Source)
at org.hsqldb.Session.execute(Unknown Source)
... 41 more
Caused by: org.hsqldb.HsqlException: transaction rollback: row change by multiple transactions
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.TransactionManagerMVCC.addInsertAction(Unknown Source)
at org.hsqldb.Session.addInsertAction(Unknown Source)
at org.hsqldb.Table.insertSingleRow(Unknown Source)
at org.hsqldb.StatementDML.insertSingleRow(Unknown Source)
at org.hsqldb.StatementInsert.getResult(Unknown Source)
at org.hsqldb.StatementDMQL.execute(Unknown Source)
... 43 more
Caused by: org.hsqldb.HsqlException: integrity constraint violation: unique constraint or index violation; JOB_INST_UN table: BATCH_JOB_INSTANCE
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.Constraint.getException(Unknown Source)
at org.hsqldb.index.IndexAVLMemory.insert(Unknown Source)
at org.hsqldb.persist.RowStoreAVL.indexRow(Unknown Source)
... 49 more
我把它归结为这个简单的配置(读取项目列表,转换为大写并打印它们),我仍然得到这个错误。
groovy
中的作业配置...
@Bean
public ItemProcessor<Object, Object> processor() {
return new ItemProcessor<Object, Object>() {
@Override
Object process(Object item) throws Exception {
return item.toString().toUpperCase()
}
}
}
@Bean
public ItemReader<Object> reader() {
return new ListItemReader<Object>(['John', 'Jill', 'James', 'Jenny'])
}
@Bean
public ItemWriter<Object> writers() {
return new ItemWriter<Object>() {
@Override
void write(List<?> items) throws Exception {
items.each {
println("List item : $it")
}
}
}
}
@Bean
public Job myJob() {
return jobBuilderFactory.get("myJob")
.incrementer(new RunIdIncrementer())
.flow(myStep1())
.end()
.build()
}
@Bean
public Step myStep1() {
return stepBuilderFactory.get("myStep1")
.<Object, Object> chunk(10)
.reader(reader())
.processor(processor())
.writer(writers())
.build()
}
这是石英豆配置(每15分钟一次的cron计划):
@Bean
public SchedulerFactoryBean scheduler() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean()
schedulerFactoryBean.setTriggers(myTrigger().getObject())
schedulerFactoryBean.start()
return schedulerFactoryBean
}
@Bean
public CronTriggerFactoryBean myTrigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean()
cronTriggerFactoryBean.setCronExpression("0 0/15 * * * ?")
cronTriggerFactoryBean.setJobDetail(myJobFactory().getObject())
return cronTriggerFactoryBean
}
@Bean
public JobDetailFactoryBean myJobFactory() {
JobDetailFactoryBean factory = new JobDetailFactoryBean(jobClass: MyJobBean,
name: MyJobBean.JOB_NAME)
return factory
}
QuartzJobBean:
@Log4j
@DisallowConcurrentExecution
class MyJobBean extends QuartzJobBean {
public static final JOB_NAME = "myBatchJob"
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("Launching $JOB_NAME")
ApplicationContext applicationContext = ApplicationContextUtil.getAppContext()
MyBatchJob myBatchJob = applicationContext.getBean(JOB_NAME, MyBatchJob.class)
try {
myBatchJob.performJob()
} catch (Exception e) {
log.warn("Quartz job failed with cause $e.message", e)
}
}
}
通过弹出批次的JobLauncher
和JobLocator
启动批处理作业的类:
@Log4j
@Component
class MyBatchJob {
@Autowired
private JobLocator jobLocator
@Autowired
private JobLauncher jobLauncher
public void performJob() {
try {
JobParameter timestampParam = new JobParameter(System.currentTimeMillis())
def paramMap = [:]
paramMap.put("timestamp", timestampParam)
JobExecution jobResult = jobLauncher.run(jobLocator.getJob("myJob"), new JobParameters(paramMap))
log.info("Job launched with result $jobResult")
} catch (Exception e) {
log.warn("Job failed with Exception ${e.getMessage()}", e.getCause())
}
}
}
spring batch admin的相关属性:
batch.job.configuration.package=com.app.jobs
batch.business.schema.script=classpath:business-schema-hsqldb.sql
ENVIRONMENT=hsqldb
石英作业因异常而失败,但Spring Batch作业运行并完成就好了。我正在考虑修复Quartz工作。任何人都看到我的配置出错了吗?
更新
当我删除Spring Batch Admin时,此错误消失了。如果我从@EnableBatchAdmin
切换回@EnableBatchProcessing
,则不会抛出异常。现在,我试图弄清楚批处理与批处理管理的默认配置之间的原因是什么。
答案 0 :(得分:0)
在我看来,new RunIdIncrementer()
行每次都会创建新的Incrementer,每次都会产生1个id,因此您在BATCH_JOB_INSTANCE表中获得了唯一约束的异常。
您需要将其存储在某个位置并且每次都可以访问它。另请参阅this answer。
答案 1 :(得分:0)
JobRepository
的隔离级别是多少?如果当前ISOLATION_READ_COMMITTED
(默认为ISOLATION_SERIALIZABLE),您可能希望将其降至ISOLATION_SERIALIZABLE
。
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:dataSource-ref="dataSource"
p:transactionManager-ref="transactionManager">
<property name="tablePrefix" value="${batch.schema}.BATCH_" />
<property name="isolationLevelForCreate" value="ISOLATION_READ_COMMITTED" />
</bean>