Spring Batch:具有Scrollable Resultset的ItemReader

时间:2013-04-23 12:40:58

标签: spring-batch

我有批处理作业,我从数据库中读取了超过100万条记录,并且我使用Scrollable Resultset访问这些记录。现在我正在将这份工作转换为春季批次。可滚动结果集在此参数中不起作用。我已经尝试但是在读取第一个块中的记录后,Resultset关闭,当批次尝试在下一步中访问它时,它会抛出异常:“无法在关闭结果集上操作”。

我是春季批次新手。任何正文都可以帮助我如何在阅读器中实现Scrollable Resultset逻辑。因为内存中的1M记录不是一个好主意。

此致

2 个答案:

答案 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。

此致