用于迭代结果集对象的 Java 流



我有以下代码片段

ResultSet rs = stmt.executeQuery(); 
List<String> userIdList = new ArrayList<String>(); 
while(rs.next()){
    userIdList.add(rs.getString(1));
}

我是否可以使用 Java 流/Lambda 表达式来执行此迭代,而不是使用 while 循环来填充列表?

您可以

ResultSet创建一个包装器,使其成为Iterable。从那里,您可以迭代以及创建流。当然,您必须定义一个映射器函数才能从结果集中获取迭代的值。

ResultSetIterable可能如下所示

public class ResultSetIterable<T> implements Iterable<T> {
  private final ResultSet rs;
  private final Function<ResultSet, T> onNext;
  public ResultSetIterable(ResultSet rs, CheckedFunction<ResultSet, T> onNext){
    this.rs = rs;
    //onNext is the mapper function to get the values from the resultSet
    this.onNext = onNext;
  }
  private boolean resultSetHasNext(){
     try {
       hasNext = rs.next();
     } catch (SQLException e) {
       //you should add proper exception handling here
       throw new RuntimeException(e);
     }
  }

  @Override
  public Iterator<T> iterator() {
    try {
        return new Iterator<T>() {
            //the iterator state is initialized by calling next() to 
            //know whether there are elements to iterate
            boolean hasNext = resultSetHasNext();

            @Override
            public boolean hasNext() {
                return hasNext;
            }
            @Override
            public T next() {
                T result = onNext.apply(rs);
                //after each get, we need to update the hasNext info
                hasNext = resultSetHasNext();
                return result;
            }
        };
    } catch (Exception e) {
        //you should add proper exception handling here
        throw new RuntimeException(e);
    }
  }
  //adding stream support based on an iteratable is easy
  public Stream<T> stream() {
    return StreamSupport.stream(this.spliterator(), false);
  }
}

现在我们有了包装器,您可以流式传输结果:

ResultSet rs = stmt.executeQuery(); 
List<String> userIdList = new ResultSetIterable(rs, rs -> rs.getString(1)).stream()
                                                                          .collect(Collectors.toList())

}

编辑

正如 Lukas 指出的那样,rs.getString(1)可能会抛出一个已检查的SQLException,因此我们需要使用一个CheckedFunction而不是一个能够将任何已检查的异常包装在未经检查的异常中的 java Function。一个非常简单的实现可能是

public interface CheckedFunction<T,R> extends Function<T,R> {
  @Override
  default R apply(T t) {
    try {
        return applyAndThrow(t);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
  }
  R applyAndThrow(T t) throws Exception;
}

或者,您可以使用具有此类函数的库,即jooλ或vavr

如果可以选择使用第三方库,则可以使用 jOOQ,它支持将 JDBC ResultSet包装在 jOOQ Cursor 类型中,然后流式传输它们。例如,使用 DSLContext.fetchStream()

从本质上讲,你可以写:

try (ResultSet rs = stmt.executeQuery()) {
    DSL.using(con)                       // DSLContext
       .fetchStream(rs)                  // Stream<Record>
       .map(r -> r.get(0, String.class)) // Stream<String>
       .collect(toList());
}

免责声明:我为供应商工作。

尝试库:abacus-jdbc

List<String> userIdList = StreamEx.<String> rows(resultSet, 1).toList(); // Don't forget to close ResultSet

或者:如果要在toList后关闭ResultSet

StreamEx.<String> rows(resultSet, 1).onClose(() -> JdbcUtil.closeQuitely(resultSet)).toList();

或者:如果使用 abacus-jdbc 中提供的实用程序类:

String sql = "select user_id from user";
// No need to worry about closing Connection/Statement/ResultSet manually. It will be took care by the framework.
JdbcUtil.prepareQuery(dataSource, sql).stream(String.class).toList();
// Or:
JdbcUtil.prepareQuery(dataSource, sql).toList(String.class);

免责声明:我是abacus-jdbc的开发者。

相关内容

  • 没有找到相关文章

最新更新