Java Streams迭代ResultSet对象

时间:2017-03-16 17:18:26

标签: java-8 java-stream resultset

我有以下代码段

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

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

3 个答案:

答案 0 :(得分:7)

您可以为ResultSet创建一个包装器,使其成为Iterable。从那里你可以迭代,也可以创建一个流。当然,您必须定义一个mapper函数来从结果集中获取迭代值。

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

答案 1 :(得分:2)

如果可以选择使用第三方库,则可以使用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());
}

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

答案 2 :(得分:0)

尝试库:abacus-jdbc

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

或者:如果您想在ResultSet之后关闭toList

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的开发人员。