我有批处理作业,我从数据库中读取了超过100万条记录,并且我使用Scrollable Resultset访问这些记录。现在我正在将这份工作转换为春季批次。可滚动结果集在此参数中不起作用。我已经尝试但是在读取第一个块中的记录后,Resultset关闭,当批次尝试在下一步中访问它时,它会抛出异常:“无法在关闭结果集上操作”。
我是春季批次新手。任何正文都可以帮助我如何在阅读器中实现Scrollable Resultset逻辑。因为内存中的1M记录不是一个好主意。
此致
答案 0 :(得分:0)
一种可能的解决方案是查看Continuable
Tasklet。 Tasklets可以“循环”由Spring Batch管理,允许您使用ResultSet.next()
检索记录并在单一访问中处理,而框架不会关闭流。
package de.incompleteco.spring.batch.step.tasklet;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
public class ScrollableResultsetTasklet implements Tasklet {
private boolean open = false;
@Resource
private DataSource dataSource;
private String sql = "select * from test_table";
private ResultSet rs;
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
if (!open) {
//open the resultset
rs = open();
open = true;//set to open
}//end if
//move
rs.next();
if (!rs.isAfterLast()) {
//show
System.out.println(rs.getInt(1));
return RepeatStatus.CONTINUABLE;
}//end if
//done
return RepeatStatus.FINISHED;
}
protected ResultSet open() throws SQLException {
return dataSource.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY).executeQuery(sql);
}
}
这当然不是'可重新启动',但后来也不是可滚动的结果集(没有一些额外的工作)。
在从块读取的情况下,您可以将结果集管理器“外化”到一个单独的pogo,它将在批处理块之间保持它自己的“状态”。这可能看起来像这样;
package de.incompleteco.spring.batch.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.sql.DataSource;
public class ScrollableResultSetService {
private boolean open = false;
@Resource
private DataSource dataSource;
private String sql = "select * from test_table";
private ResultSet rs;
/**
* retrieve the result set in a 'next' state
* @return
* @throws SQLException
*/
public ResultSet getNext() throws SQLException {
if (rs == null || !open) {
//open the resulset
rs = dataSource.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY).executeQuery(sql);
}//end if
//move
rs.next();
//test
if (rs.isAfterLast()) {
return null;
}//end if
return rs;
}
}
然后,itemreader看起来有点像这样;
package de.incompleteco.spring.batch.step.item;
import java.sql.ResultSet;
import javax.annotation.Resource;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import de.incompleteco.spring.batch.service.ScrollableResultSetService;
public class ScrollableResultSetItemReader implements ItemReader<T> {
@Resource
private ScrollableResultSetService service;
@Override
public T read() throws Exception, UnexpectedInputException, ParseException,NonTransientResourceException {
ResultSet rs = service.getNext();
if (rs == null) {
return null;//don't continue
}//end if
//process the result set into your object
//...
//return object
return T;
}
}
答案 1 :(得分:0)
您可以使用现成的JdbcPagingItemReader或JdbcCursorItemReader。尝试两者,看看哪一个最适合你。
您的实施将与两者相同..仅配置更改! ; - )
它们都让你读取非常大量的记录而不将它们全部留在内存中。
1M记录与Spring Batch无关......如果您使用正确的阅读器; - )
编辑:根据您的意见:
您能提供现有界面/ Impl的代码示例吗?
通常,当您想使用现有服务/ Dao时,可以使用ItemReaderAdapter。这将允许您定义委托read()的对象和方法。但开箱即用的实现是非常基本的,并将在启动时调用您的自定义service.find()(InitializingBean)。所以你最终会在内存列表中得到1M对象!
因为find方法可能会运行查询并将resultSet映射到List
我建议你将SQL从实现类移到JdbcCursorItemreader,并使用stepListener来验证jobParameters并在JdbcCursorItemreader中设置正确的Sql。
此致