我有一份从SQL Server数据库文档列表中读取的作业。文档需要处于某种状态,并按列status_updated_time
进行排序。
我想读取document.id
,然后在作业处理器中将其处理为Driving Query Based ItemReaders。
列状态在writer中已更改,因此由于this problem而无法使用JpaPagingItemReader
。
我使用了JdbcPagingItemReader
,但是在按status_updated_time
进行排序时出现了错误。
然后,我尝试添加id
并进行排序,但这没有帮助。
我要获取的查询是:
SELECT id
FROM document
WHERE status IN (0, 1, 2)
ORDER BY status_updated_time ASC, id ASC
我的读者:
@StepScope
@Bean
private ItemReader<Long> statusReader() {
JdbcPagingItemReader<Long> reader = new JdbcPagingItemReader<>();
...
reader.setRowMapper(SingleColumnRowMapper.newInstance(Long.class));
...
Map<String, Order> sortKeys = new HashMap<>();
sortKeys.put("status_updated_time", Order.ASCENDING);
sortKeys.put("id", Order.ASCENDING);
SqlServerPagingQueryProvider queryProvider = new SqlServerPagingQueryProvider();
queryProvider.setSelectClause(SELECT_CLAUSE);
queryProvider.setFromClause(FROM_CLAUSE);
queryProvider.setWhereClause(WHERE_CLAUSE);
queryProvider.setSortKeys(sortKeys);
reader.setQueryProvider(queryProvider);
...
return reader;
}
常量为:
private static final String SELECT_CLAUSE = "id";
private static final String FROM_CLAUSE = "document";
private static final String WHERE_CLAUSE = "status IN (0, 1, 2) ";
执行作业时出现错误:
org.springframework.dao.TransientDataAccessResourceException: StatementCallback; SQL [SELECT TOP 10 id FROM document WHERE status IN (0, 1, 2) ORDER BY id ASC, status_updated_time ASC]; The column name status_updated_time is not valid.; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: The column name status_updated_time is not valid.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:110)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:388)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:452)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:462)
at org.springframework.batch.item.database.JdbcPagingItemReader.doReadPage(JdbcPagingItemReader.java:210)
at org.springframework.batch.item.database.AbstractPagingItemReader.doRead(AbstractPagingItemReader.java:108)
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:92)
at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:94)
at org.springframework.batch.core.step.item.FaultTolerantChunkProvider.read(FaultTolerantChunkProvider.java:87)
at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:119)
我在栈溢出(this ...)上遇到了关于The column name XYZ is not valid
的一些问题,但是在需要按另一列排序的情况下,没有看到任何有效的方法。
另一个问题是排序列顺序。
无论我是先将status_updated_time
还是id
添加到生成的脚本中的地图排序中,始终是ORDER BY id ASC, status_updated_time ASC
。
编辑: 阅读this question,尤其是这一行:
JdbcPagingItemReader假定排序键和select子句中的列完全相同
我意识到我需要结果集中的列status_updated_time
,所以我进行了重构:
private static final String SELECT_CLAUSE = "id, status_updated_time";
...
queryProvider.setSelectClause(SELECT_CLAUSE);
...
reader.setRowMapper(
(rs, i) -> {
Document document = new Document();
document.setId(rs.getLong(1));
document.setStatusUpdatedTime(rs.getObject(2, Timestamp.class));
return document;
}
);
现在应用程序可以编译,作业可以运行了。
但是,排序问题仍然存在。我无法先订购status_updated_time
,然后订购id
。 id
永远是第一位。
我试图从排序中删除id
并遇到另一个问题。
在测试环境中。我有1600行要处理。我的工作流程行并将status_updated_time
更新为now()
。作业开始处理时,他并没有停止在1600,而是继续处理,因为每一行都有新的status_updated_time
,并且读者将其视为新行,并不断进行处理。
仅按id
排序时,作业处理了1600行,然后停止了。
看来由于排序问题,我无法使用JdbcPagingItemReader
。
而且我想要一些可以并行运行的阅读器以加快这项工作(每天每小时运行约20分钟)。
有什么建议吗?
答案 0 :(得分:0)
我要感谢Mahmoud监视Spring Batch问题并为您提供帮助。但是他的建议没有帮助我,所以我使用了不同的方法。 我使用临时(辅助)表来准备要执行主要步骤的数据,并且在主要步骤中,读者正在从该表中读取数据。
第一步将删除帮助表:
enwiki-latest-page.sql
第二步将准备数据。插入文档ID,将在以后处理:
@Bean
private Step dropHelpTable() {
return stepBuilderFactory
.get(STEP_DROP_HELP_TABLE)
.transactionManager(cronTransactionManager)
.tasklet(dropHelpTableTasklet())
.build();
}
private Tasklet dropHelpTableTasklet() {
return (contribution, chunkContext) -> {
jdbcTemplate.execute(DROP_SCRIPT);
return RepeatStatus.FINISHED;
};
}
private static final String STEP_DROP_HELP_TABLE = "dropHelpTable";
private static final String DROP_SCRIPT = "IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES "
+ "WHERE TABLE_NAME = 'query_document_helper') "
+ "BEGIN "
+ " DROP TABLE query_document_helper "
+ "END";
此后,我掌握了将在一个作业执行中使用的所有数据,因此不再需要按status_updated_time进行排序(条件不是在此作业执行中处理最年轻的文档,而是在它们变得最旧时在以后的执行中处理)。 然后在下一步中,使用常规阅读器。
@Bean
private Step insertDataToHelpTable() {
return stepBuilderFactory
.get(STEP_INSERT_HELP_TABLE)
.transactionManager(cronTransactionManager)
.tasklet(insertDataToHelpTableTasklet())
.build();
}
private Tasklet insertDataToHelpTableTasklet() {
return (contribution, chunkContext) -> {
jdbcTemplate.execute("SELECT TOP " + limit + " id " + INSERT_SCRIPT);
return RepeatStatus.FINISHED;
};
}
private static final String STEP_INSERT_HELP_TABLE = "insertHelpTable";
private static final String INSERT_SCRIPT = "INTO query_document_helper "
+ "FROM dbo.document "
+ "WHERE status IN (0, 1, 2) "
+ "ORDER BY status_updated_time ASC";
@Value("${cron.batchjob.queryDocument.limit}")
private Integer limit;
工作看起来像这样:
@Bean
private Step queryDocumentStep() {
return stepBuilderFactory
.get(STEP_QUERY_NEW_DOCUMENT_STATUS)
.transactionManager(cronTransactionManager)
.<Long, Document>chunk(chunk)
.reader(documentReader())
...
.taskExecutor(multiThreadingTaskExecutor.threadPoolTaskExecutor())
.build();
}
@StepScope
@Bean
private ItemReader<Long> documentReader() {
JdbcPagingItemReader<Long> reader = new JdbcPagingItemReader<>();
reader.setDataSource(coreBatchDataSource);
reader.setMaxItemCount(limit);
reader.setPageSize(chunk);
...
Map<String, Order> sortKeys = new HashMap<>();
sortKeys.put("id", Order.ASCENDING);
SqlServerPagingQueryProvider queryProvider = new SqlServerPagingQueryProvider();
queryProvider.setSelectClause(SELECT_CLAUSE);
queryProvider.setFromClause(FROM_CLAUSE);
queryProvider.setSortKeys(sortKeys);
reader.setQueryProvider(queryProvider);
...
return reader;
}
private static final String STEP_QUERY_NEW_DOCUMENT_STATUS = "queryNewDocumentStatus";
private static final String SELECT_CLAUSE = "id";
private static final String FROM_CLAUSE = "query_archive_document_helper";