Spring 批处理 JdbcPagingItemReader 似乎没有分页
Spring Batch JdbcPagingItemReader seems not paginating
我正在开发一个应用程序,该应用程序在嵌入式 Derby(10.12.1.1) 数据库中为一堆文件编制索引,然后导出为一个单独的表格文件。第一部分工作正常,因为数据库已创建并且所有记录都已编入索引。
但是,当我尝试使用 JdbcPagingItemReader
从数据库读取并写入文件时,我只获得了 pageSize
中指定的记录数。所以如果 pageSize
是 10,那么我得到一个包含 10 行的文件,其余的记录似乎被忽略了。到目前为止,我还没有找到真正发生的事情,欢迎任何帮助。
这是 JdbcPagingItemReader
配置:
<!-- DB Item Reader -->
<bean id="pagingItemReader" class="org.springframework.batch.item.database.JdbcPagingItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="queryProvider">
<bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="selectClause">
<value>
<![CDATA[
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
]]>
</value>
</property>
<property name="fromClause">
<value>
<![CDATA[
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
]]>
</value>
</property>
<property name="sortKey" value="ID" />
</bean>
</property>
<property name="pageSize" value="100" />
<property name="rowMapper">
<bean class="org.miabis.converter.batch.database.SampleRowMapper" />
</property>
</bean>
这是作业配置:
<batch:job id="job1">
<batch:step id="step1" next="step2">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="contactInfoReader" writer="contactInfoWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step2" next="step3">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="biobankReader" writer="biobankWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step3" next="step4">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="sampleCollectionReader" writer="sampleCollectionWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step4" next="step5">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="studyReader" writer="studyWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step5" next="step6">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="sampleReader" writer="sampleWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step6">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="pagingItemReader" writer="tabFileItemWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
</batch:job>
编辑
根据 Hansjoerg Wingeier 的建议,我将第 6 步中的 commit-interval
与 JdbcPagingItemReader
配置中的 pageSize
相匹配。此外,我扩展了 JdbcPagingItemReader
只是为了记录调用方法 doReadPage 的次数。事实证明,它只调用了两次,但 rowmapper 只从第一页获取项目。
此外,我尝试使用 JdbcCursorItemReader,但它抛出一个 SQL 异常,显示 The 'getRow()' 方法仅被允许在滚动光标上.
这是 JdbcCursorItemReader 配置:
<bean id="itemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
select
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
from
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
]]>
</value>
</property>
<property name="rowMapper">
<bean class="org.miabis.converter.batch.database.SampleRowMapper" />
</property>
</bean>
这里是完整的例外:
[main] INFO org.springframework.batch.core.job.SimpleStepHandler - Executing step: [step6]
[main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
[main] INFO org.springframework.jdbc.support.SQLErrorCodesFactory - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
[main] ERROR org.springframework.batch.core.step.AbstractStep - Encountered an error executing step step6 in job job1
org.springframework.jdbc.UncategorizedSQLException: Attempt to process next row failed; uncategorized SQLException for SQL [
select
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
from
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
]; SQL state [XJ061]; error code [20000]; The 'getRow()' method is only allowed on scroll cursors.; nested exception is java.sql.SQLException: The 'getRow()' method is only allowed on scroll cursors.
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.batch.item.database.AbstractCursorItemReader.doRead(AbstractCursorItemReader.java:456)
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:88)
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.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy18.read(Unknown Source)
at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:91)
at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:157)
at org.springframework.batch.core.step.item.SimpleChunkProvider.doInIteration(SimpleChunkProvider.java:116)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:110)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:69)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.springframework.batch.core.step.tasklet.TaskletStep.doInChunkContext(TaskletStep.java:271)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:64)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:134)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:135)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
at org.miabis.converter.JobFilesDBTabTest.testLaunchJob(JobFilesDBTabTest.java:43)
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.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.sql.SQLException: The 'getRow()' method is only allowed on scroll cursors.
at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedResultSet.checkScrollCursor(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedResultSet.getRow(Unknown Source)
at org.springframework.batch.item.database.AbstractCursorItemReader.verifyCursorPosition(AbstractCursorItemReader.java:367)
at org.springframework.batch.item.database.AbstractCursorItemReader.doRead(AbstractCursorItemReader.java:452)
... 71 more
Caused by: ERROR XJ061: The 'getRow()' method is only allowed on scroll cursors.
at org.apache.derby.iapi.error.StandardException.newException(Unknown Source)
at org.apache.derby.impl.jdbc.SQLExceptionFactory.wrapArgsForTransportAcrossDRDA(Unknown Source)
... 79 more
根据 Hansjoerg Wingeier 的建议,我决定使用 JdbcCursorItemReader
并通过设置 verifyCursorPosition=false 解决了 UncategorizedSQLException here.
这是 JdbcCursorItemReader 的最终配置:
<bean id="jdbcCursorItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
select
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
from
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
order by SAMPLE.ID
]]>
</value>
</property>
<property name="rowMapper">
<bean class="org.miabis.converter.batch.database.SampleRowMapper" />
</property>
<property name="verifyCursorPosition" value="false"/>
</bean>
我正在开发一个应用程序,该应用程序在嵌入式 Derby(10.12.1.1) 数据库中为一堆文件编制索引,然后导出为一个单独的表格文件。第一部分工作正常,因为数据库已创建并且所有记录都已编入索引。
但是,当我尝试使用 JdbcPagingItemReader
从数据库读取并写入文件时,我只获得了 pageSize
中指定的记录数。所以如果 pageSize
是 10,那么我得到一个包含 10 行的文件,其余的记录似乎被忽略了。到目前为止,我还没有找到真正发生的事情,欢迎任何帮助。
这是 JdbcPagingItemReader
配置:
<!-- DB Item Reader -->
<bean id="pagingItemReader" class="org.springframework.batch.item.database.JdbcPagingItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="queryProvider">
<bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="selectClause">
<value>
<![CDATA[
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
]]>
</value>
</property>
<property name="fromClause">
<value>
<![CDATA[
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
]]>
</value>
</property>
<property name="sortKey" value="ID" />
</bean>
</property>
<property name="pageSize" value="100" />
<property name="rowMapper">
<bean class="org.miabis.converter.batch.database.SampleRowMapper" />
</property>
</bean>
这是作业配置:
<batch:job id="job1">
<batch:step id="step1" next="step2">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="contactInfoReader" writer="contactInfoWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step2" next="step3">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="biobankReader" writer="biobankWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step3" next="step4">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="sampleCollectionReader" writer="sampleCollectionWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step4" next="step5">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="studyReader" writer="studyWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step5" next="step6">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="sampleReader" writer="sampleWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
<batch:step id="step6">
<batch:tasklet transaction-manager="transactionManager" start-limit="100" >
<batch:chunk reader="pagingItemReader" writer="tabFileItemWriter" commit-interval="100" />
</batch:tasklet>
</batch:step>
</batch:job>
编辑
根据 Hansjoerg Wingeier 的建议,我将第 6 步中的 commit-interval
与 JdbcPagingItemReader
配置中的 pageSize
相匹配。此外,我扩展了 JdbcPagingItemReader
只是为了记录调用方法 doReadPage 的次数。事实证明,它只调用了两次,但 rowmapper 只从第一页获取项目。
此外,我尝试使用 JdbcCursorItemReader,但它抛出一个 SQL 异常,显示 The 'getRow()' 方法仅被允许在滚动光标上.
这是 JdbcCursorItemReader 配置:
<bean id="itemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
select
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
from
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
]]>
</value>
</property>
<property name="rowMapper">
<bean class="org.miabis.converter.batch.database.SampleRowMapper" />
</property>
</bean>
这里是完整的例外:
[main] INFO org.springframework.batch.core.job.SimpleStepHandler - Executing step: [step6]
[main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
[main] INFO org.springframework.jdbc.support.SQLErrorCodesFactory - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
[main] ERROR org.springframework.batch.core.step.AbstractStep - Encountered an error executing step step6 in job job1
org.springframework.jdbc.UncategorizedSQLException: Attempt to process next row failed; uncategorized SQLException for SQL [
select
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
from
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
]; SQL state [XJ061]; error code [20000]; The 'getRow()' method is only allowed on scroll cursors.; nested exception is java.sql.SQLException: The 'getRow()' method is only allowed on scroll cursors.
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.batch.item.database.AbstractCursorItemReader.doRead(AbstractCursorItemReader.java:456)
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:88)
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.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy18.read(Unknown Source)
at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:91)
at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:157)
at org.springframework.batch.core.step.item.SimpleChunkProvider.doInIteration(SimpleChunkProvider.java:116)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:110)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:69)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.springframework.batch.core.step.tasklet.TaskletStep.doInChunkContext(TaskletStep.java:271)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:64)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:134)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:135)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
at org.miabis.converter.JobFilesDBTabTest.testLaunchJob(JobFilesDBTabTest.java:43)
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.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.sql.SQLException: The 'getRow()' method is only allowed on scroll cursors.
at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedResultSet.checkScrollCursor(Unknown Source)
at org.apache.derby.impl.jdbc.EmbedResultSet.getRow(Unknown Source)
at org.springframework.batch.item.database.AbstractCursorItemReader.verifyCursorPosition(AbstractCursorItemReader.java:367)
at org.springframework.batch.item.database.AbstractCursorItemReader.doRead(AbstractCursorItemReader.java:452)
... 71 more
Caused by: ERROR XJ061: The 'getRow()' method is only allowed on scroll cursors.
at org.apache.derby.iapi.error.StandardException.newException(Unknown Source)
at org.apache.derby.impl.jdbc.SQLExceptionFactory.wrapArgsForTransportAcrossDRDA(Unknown Source)
... 79 more
根据 Hansjoerg Wingeier 的建议,我决定使用 JdbcCursorItemReader
并通过设置 verifyCursorPosition=false 解决了 UncategorizedSQLException here.
这是 JdbcCursorItemReader 的最终配置:
<bean id="jdbcCursorItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
select
SAMPLE.ID, SAMPLE.PARENT_SAMPLE_ID, SAMPLE.MATERIAL_TYPE, SAMPLE.CONTAINER, SAMPLE.STORAGE_TEMPERATURE, SAMPLE.SAMPLED_TIME, SAMPLE.ANATOMICAL_SITE_ONTOLOGY, SAMPLE.ANATOMICAL_SITE_VERSION, SAMPLE.ANATOMICAL_SITE_CODE, SAMPLE.ANATOMICAL_SITE_DESCRIPTION, SAMPLE.ONTOLOGY_ONTOLOGY, SAMPLE.ONTOLOGY_VERSION, SAMPLE.ONTOLOGY_CODE, SAMPLE.ONTOLOGY_DESCRIPTION, SAMPLE.ONTOLOGY_FREE_TEXT, SAMPLE.SEX, SAMPLE.AGE_LOW, SAMPLE.AGE_HIGH, SAMPLE.AGE_UNIT,
BIOBANK.ID AS BIOBANK_ID, BIOBANK.ACRONYM, BIOBANK.NAME, BIOBANK.URL, BIOBANK.JURISTIC_PERSON, BIOBANK.COUNTRY AS BIOBANK_COUNTRY, BIOBANK.DESCRIPTION,
BB_CI.ID AS BB_CI_ID, BB_CI.FIRST_NAME AS BB_CI_FIRST_NAME, BB_CI.LAST_NAME AS BB_CI_LAST_NAME, BB_CI.PHONE AS BB_CI_PHONE, BB_CI.EMAIL AS BB_CI_EMAIL, BB_CI.ADDRESS AS BB_CI_ADDRESS, BB_CI.ZIP AS BB_CI_ZIP, BB_CI.CITY AS BB_CI_CITY, BB_CI.COUNTRY AS BB_CI_COUNTRY,
STUDY.ID AS STUDY_ID, STUDY.NAME AS STUDY_NAME, STUDY.DESCRIPTION AS STUDY_DESCRIPTION, STUDY.STUDY_DESIGN AS STUDY_STUDY_DESIGN, STUDY.DATA_CATEGORY AS STUDY_DATA_CATEGORY, STUDY.TOTAL_NUMBER_OF_PARTICIPANTS, STUDY.TOTAL_NUMBER_OF_DONORS, STUDY.INCLUSION_CRITERIA, STUDY.PRINCIPAL_INVESTIGATOR,
S_CI.ID AS S_CI_ID, S_CI.FIRST_NAME AS S_CI_FIRST_NAME, S_CI.LAST_NAME AS S_CI_LAST_NAME, S_CI.PHONE AS S_CI_PHONE, S_CI.EMAIL AS S_CI_EMAIL, S_CI.ADDRESS AS S_CI_ADDRESS, S_CI.ZIP AS S_CI_ZIP, S_CI.CITY AS S_CI_CITY, S_CI.COUNTRY AS S_CI_COUNTRY,
SAMPLE_COLLECTION.ID AS SAMPLE_COLLECTION_ID, SAMPLE_COLLECTION.ACRONYM AS SAMPLE_COLLECTION_ACRONYM, SAMPLE_COLLECTION.NAME AS SAMPLE_COLLECTION_NAME, SAMPLE_COLLECTION.DESCRIPTION AS SAMPLE_COLLECTION_DESCRIPTION, SAMPLE_COLLECTION.DATA_CATEGORY AS SAMPLE_COLLECTION_DATA_CATEGORY, SAMPLE_COLLECTION.COLLECTION_TYPE AS SAMPLE_COLLECTION_COLLECTION_TYPE,
SC_CI.ID AS SC_CI_ID, SC_CI.FIRST_NAME AS SC_CI_FIRST_NAME, SC_CI.LAST_NAME AS SC_CI_LAST_NAME, SC_CI.PHONE AS SC_CI_PHONE, SC_CI.EMAIL AS SC_CI_EMAIL, SC_CI.ADDRESS AS SC_CI_ADDRESS, SC_CI.ZIP AS SC_CI_ZIP, SC_CI.CITY AS SC_CI_CITY, SC_CI.COUNTRY AS SC_CI_COUNTRY
from
SAMPLE
LEFT JOIN BIOBANK ON SAMPLE.BIOBANK = BIOBANK.ID
LEFT JOIN CONTACT_INFORMATION AS BB_CI ON BIOBANK.CONTACT_INFORMATION = BB_CI.ID
LEFT JOIN STUDY ON SAMPLE.STUDY = STUDY.ID
LEFT JOIN CONTACT_INFORMATION AS S_CI ON STUDY.CONTACT_INFORMATION = S_CI.ID
LEFT JOIN SAMPLE_COLLECTION ON SAMPLE.SAMPLE_COLLECTION = SAMPLE_COLLECTION.ID
LEFT JOIN CONTACT_INFORMATION AS SC_CI ON SAMPLE_COLLECTION.CONTACT_INFORMATION = SC_CI.ID
order by SAMPLE.ID
]]>
</value>
</property>
<property name="rowMapper">
<bean class="org.miabis.converter.batch.database.SampleRowMapper" />
</property>
<property name="verifyCursorPosition" value="false"/>
</bean>