Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Java流使用数据库游标_Java_Java Stream_Ram_Database Cursor - Fatal编程技术网

使用Java流使用数据库游标

使用Java流使用数据库游标,java,java-stream,ram,database-cursor,Java,Java Stream,Ram,Database Cursor,我想使用Java流使用数据库游标。我希望Java流能够根据需要获取和处理行,并避免先在内存中加载所有500万行,然后再进行处理 是否可以在不将整个表加载到RAM中的情况下使用它 到目前为止,我的代码如下所示: Cursor<Product> products = DAO.selectCursor(...); // 1. Initialize variables long count = 0; ... for (Iterator<Product> it = product

我想使用Java流使用数据库游标。我希望Java流能够根据需要获取和处理行,并避免先在内存中加载所有500万行,然后再进行处理

是否可以在不将整个表加载到RAM中的情况下使用它

到目前为止,我的代码如下所示:

Cursor<Product> products = DAO.selectCursor(...);

// 1. Initialize variables
long count = 0;
...
for (Iterator<Product> it = products.iterator(); it.hasNext();) {
  Product p = it.next();
  // 2. Processing each row
  ...
}
// 3. Concluding (processing totals, stats, etc.)
double avg = total / count;
...
cursorproducts=DAO.selectCursor(…);
// 1. 初始化变量
长计数=0;
...
for(Iterator it=products.Iterator();it.hasNext();){
产品p=it.next();
//2.处理每一行
...
}
// 3. 总结(处理总数、统计数据等)
平均双倍=总数/计数;
...

它确实工作得很好,但有点麻烦,我想利用Stream API。

首先,我们必须讨论如何从数据库中获取数据。如果您的目的是浏览大量记录,而不希望在内存中同时加载所有记录,则有两种选择:

  • 将结果分页
  • 让您的驱动程序对结果进行分页

  • 如果您已经有了一个基于
    游标的迭代器
    ,可以根据需要检索分页数据,那么您可以使用JDK API中的
    拆分器
    流支持
    实用程序类将其转换为

    Stream products=StreamSupport.Stream(
    Spliterators.spliteratorUnknownSize(cursor.iterator(),
    Spliterator.NONNULL|
    分路器|
    拆分器(不可变),false)
    
    否则,你将不得不建立自己的东西

    驱动程序分页 如果JDBC驱动程序支持fetch size属性,则可以执行以下操作:

    Connection con = ds.getConnection();
    con.setAutoCommit(false);
    PreparedStatement stm = con.prepareStatement("SELECT order_number FROM orders WHERE order_date >= '2018-08-12'", ResultSet.TYPE_FORWARD_ONLY);
    stm.setFetchSize(1000);
    ResultSet rs = stm.executeQuery();
    
    此时,
    rs
    包含1000条记录的第一次获取,在您阅读上一页之前,它不会从数据库中获取更多记录

    所有这一切的棘手之处在于,在读取完所有记录之前,您无法关闭任何资源(即连接、预处理语句和结果集),并且由于我们要构建的流在默认情况下是惰性的,这意味着我们必须保持所有这些资源处于打开状态,直到处理完该流

    也许最简单的方法是围绕此逻辑构建一个迭代器,当迭代器实际到达所有数据的末尾时,您可以关闭所有资源(即
    !rs.next()
    ),或者另一种方法是在流关闭时完成所有工作(
    stream.onClose()

    一旦有了迭代器,使用JDK API中的
    Spliterators
    StreamSupport
    实用程序类从迭代器中构建流就非常简单了

    我的基本实现看起来有点像这样。这只是为了举例说明。你可能想对你的特殊情况给予更多的关爱

    公共流getUsers(){
    DataSource ds=jdbcTemplate.getDataSource();
    试一试{
    连接conn=ds.getConnection();
    连接设置自动提交(错误);
    PreparedStatement stm=conn.prepareStatement(“从用户中选择id”,结果集。仅键入“前进”);
    //fetch size可以保证一次只有1000条记录
    stm.setFetchSize(1000);
    ResultSet rs=stm.executeQuery();
    迭代器sqlIter=新迭代器(){
    @凌驾
    公共布尔hasNext(){
    试一试{
    返回rs.next();
    }捕获(SQLE异常){
    封闭资源(conn、stm、rs);
    抛出新的RuntimeException(“未能从ResultSet中读取记录”,e);
    }
    }
    @凌驾
    公共字符串next(){
    试一试{
    返回rs.getString(“id”);
    }捕获(SQLE异常){
    封闭资源(conn、stm、rs);
    抛出新的RuntimeException(“未能从ResultSet中读取记录”,e);
    }
    }
    };
    //将迭代器转换为流
    returnstreamsupport.stream(
    Spliterators.spliteratorUnknownSize(sqlIter,
    Spliterator.NONNULL|
    分路器|
    拆分器(不可变),false
    ).onClose(()->{
    //确保在处理流时关闭资源
    封闭资源(conn、stm、rs);
    });
    }捕获(SQLE异常){
    记录器错误(“处理数据失败”,e);
    抛出新的运行时异常(e);
    }
    }
    私有void closeResources(连接连接、准备的语句ps、结果集rs){
    try(conn;ps;rs){
    logger.info(“资源已成功关闭”);
    }捕获(SQLE异常){
    logger.warn(“未能正确关闭数据库源”,e);
    }
    }
    
    这里的关键点是要注意,我们返回的流应该运行一些
    onClose
    逻辑,因此,当我们使用流时,必须确保在使用它时执行
    stream.close()
    ,以确保我们关闭到这一点为止保持活动的所有资源(即
    conn
    stm
    rs

    最好的方法可能是使用try和资源,这样try将负责关闭流

    try(Stream users=userRepo.getUsers()){
    //将用户打印到主输出,同时检索1K
    users.forEach(System.out::println);
    }
    
    手动分页 另一种方法是您自己对结果进行分页,这取决于数据库,但使用诸如limit和offset之类的select子句,您可以请求特定的记录页,处理它们,然后检索更多的记录

    从用户限制中选择id 1000偏移量5
    
    在这种情况下,迭代器将消耗所有页面,完成后,请求下一个页面,直到不再重新生成