我想使用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;
...
它确实工作得很好,但是有点麻烦,我想利用流应用编程接口。
首先,我们必须讨论如何从数据库中获取数据。如果您的目的是浏览大量记录,而不希望在内存中同时加载所有记录,则有两种选择:
如果您已经有了一个基于游标的迭代器
,可以根据需要检索分页数据,那么您可以使用JDK API中的拆分器
和流支持
实用程序类将其转换为流
。
Stream<Product> products = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(cursor.iterator(),
Spliterator.NONNULL |
Spliterator.ORDERED |
Spliterator.IMMUTABLE), 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
实用程序类从迭代器中构建流就非常简单了。
我的基本实现看起来有点像这样。这只是为了说明的目的。你可能想给你的特殊情况更多的爱。
public Stream<String> getUsers() {
DataSource ds = jdbcTemplate.getDataSource();
try {
Connection conn = ds.getConnection();
conn.setAutoCommit(false);
PreparedStatement stm = conn.prepareStatement("SELECT id FROM users", ResultSet.TYPE_FORWARD_ONLY);
//fetch size is what guarantees only 1000 records at the time
stm.setFetchSize(1000);
ResultSet rs = stm.executeQuery();
Iterator<String> sqlIter = new Iterator<>() {
@Override
public boolean hasNext() {
try {
return rs.next();
} catch (SQLException e) {
closeResources(conn, stm, rs);
throw new RuntimeException("Failed to read record from ResultSet", e);
}
}
@Override
public String next() {
try {
return rs.getString("id");
} catch (SQLException e) {
closeResources(conn, stm, rs);
throw new RuntimeException("Failed to read record from ResultSet", e);
}
}
};
//turn iterator into a stream
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(sqlIter,
Spliterator.NONNULL |
Spliterator.ORDERED |
Spliterator.IMMUTABLE), false
).onClose(() -> {
//make sure to close resources when done with the stream
closeResources(conn, stm, rs);
});
} catch (SQLException e) {
logger.error("Failed to process data", e);
throw new RuntimeException(e);
}
}
private void closeResources(Connection conn, PreparedStatement ps, ResultSet rs) {
try (conn; ps; rs) {
logger.info("Resources successfully closed");
} catch (SQLException e) {
logger.warn("Failed to properly close database sources", e);
}
}
这里的关键点是要注意,我们返回的流应该运行一些onClose
逻辑,因此当我们使用流时,必须确保执行流。close()
当我们完成它时,以确保我们关闭到目前为止保持活动状态的所有资源(即conn
、stm
和rs
)。
最好的方法也许是使用资源的try,这样try将负责关闭流。
try(Stream<String> users = userRepo.getUsers()){
//print users to the main output retrieving 1K at the time
users.forEach(System.out::println);
}
另一种方法是您自己对结果进行分页,这取决于数据库,但使用诸如limit和offset之类的select子句,您可以请求特定的记录页,处理它们,然后检索更多的记录。
select id from users LIMIT 1000 OFFSET 5
在这种情况下,迭代器将消耗所有页面,完成后,请求下一页,直到在最后一页中没有找到更多记录。
这种另一种方法的优点是资源可以在迭代器本身中立即控制。
我不会开发一个这样的例子,留给你去尝试。
本文向大家介绍mongodb数据库游标的使用浅析,包括了mongodb数据库游标的使用浅析的使用技巧和注意事项,需要的朋友参考一下 mongodb中的游标使用示例如下: 假设执行如下操作: 使用find()返回一个游标: 使用游标的forEach()循环遍历:
本章节将介绍如何创建一个从数据表 country 中读取国家数据并显示出来的页面。 为了实现这个目标,你将会配置一个数据库连接, 创建一个活动记录类, 并且创建一个操作及一个视图。 贯穿整个章节,你将会学到: 配置一个数据库连接 定义一个活动记录类 使用活动记录从数据库中查询数据 以分页方式在视图中显示数据 请注意,为了掌握本章你应该具备最基本的数据库知识和使用经验。 尤其是应该知道如何创建数据库
使用 JDBC 连接数据库 JAVA应用要连接到数据库,首先需要加载数据库驱动,然后获得一个数据库连接,下面是一个简单的例子: import java.sql.*; public class Test { public static void main(String[] a) throws Exception { Class.forName("org
本文向大家介绍JAVA使用DBUtils操作数据库,包括了JAVA使用DBUtils操作数据库的使用技巧和注意事项,需要的朋友参考一下 摘要:本文主要学习了如何使用DBUtils在Java代码中更方便的操作数据库。 概述 DBUtils是Java编程中的数据库操作实用工具,小巧简单实用。 DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。 使用 准备 如果需要使用DBUtil
SQLAlchemy 1.4 / 2.0 Tutorial 此页是 SQLAlchemy 1.4/2.0教程 . 上一页: 处理事务和DBAPI |下一步: |next| 使用数据库元数据 随着引擎和SQL执行的停止,我们准备开始一些炼金术。SQLAlchemy Core和ORM的核心元素是SQL表达式语言,它允许流畅、可组合地构造SQL查询。这些查询的基础是表示数据库概念(如表和列)的Pytho
数据库:Oracle 11gr2Web应用:JSP(Java)我有两个表: IdCity_FK引用来自“城市”的列 Id。 问题:如何从具有以下字段的插入.jsp页面插入数据:人名,城市。 我希望用户输入自己的名字,并从动态生成的列表框中选择一个城市(使用游标Oracle或其他方法)。< br >例如,不输入:Andrew,12,而是输入Andrew并选择New York。我使用Oracle RE