我有以下代码片段
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的开发者。