当前位置:网站首页>在jOOQ中获取数据的多种不同方式
在jOOQ中获取数据的多种不同方式
2022-07-30 19:33:00 【JAVAQXQ】
jOOQ的API是关于方便的,因此,像 fetch() 这样的重要操作(最重要的操作?)也必须附带方便。获取数据的默认方式是这样的:
Result<Record1<String>> result =
ctx.select(BOOK.TITLE)
.from(BOOK)
.fetch();
for (Record1<String> record : result) {
// ...
}
复制代码它将整个结果集取到内存中,并急切地关闭底层的JDBC资源。但是我们还有什么其他的选择呢?
可迭代的获取方式
在上面的例子中, fetch() 的调用并不是严格意义上的必要。 [ResultQuery<R>](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html) 类型 方便地扩展了 Iterable<R> , 这意味着对 ResultQuery.iterator() 的调用也将执行该查询。这主要可以通过两种方式实现。
外部迭代
for (Record1<String> record : ctx
.select(BOOK.TITLE)
.from(BOOK)
) {
// ...
}
复制代码这特别好,因为它感觉就像PL/SQL或PL/pgSQL的 FOR 循环,用于隐式游标:
FOR rec IN (SELECT book.title FROM book) LOOP -- ... END LOOP; 复制代码
不过这仍然要把整个结果集取到内存中,因为在Java中没有一个 for-with-resources 语法,它把 foreach 语法和 try-with-resources 语法结合起来。
内部迭代
JDK 8增加了 Iterable::forEach ,jOOQ的 [ResultQuery](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html) 继承了,所以你也可以这样做。
ctx.select(BOOK.TITLE)
.from(BOOK)
.forEach(record -> {
// ...
});
复制代码两者是完全等价的。
单一记录的获取
如果你确定你只取一个单一的值,不需要具体化一个列表。只需使用以下方法之一。鉴于这个查询:
ResultQuery<Record1<String>> query = ctx
.select(BOOK.TITLE)
.from(BOOK)
.where(BOOK.ID.eq(1));
复制代码你现在可以
取一个可空的记录
这就获取了一个可空的记录,也就是说,如果没有找到记录,就会产生 null 。如果有一条以上的记录,就会产生一个 [TooManyRowsException](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/exception/TooManyRowsException.html) 会被抛出。
Record1<String> r = query.fetchOne(); 复制代码
取一个可选择的记录
null 自行车棚是真实的,那么为什么在使用jOOQ的时候不让你也骑自行车呢?与上述完全等同,但使用不同的风格,是这样的。
Optional<Record1<String>> r = query.fetchOptional(); 复制代码
取出一条记录
如果你知道你的查询正好产生一条记录,在jOOQ的API中有一个术语 "single",意思是正好一条:
Record1<String> r = query.fetchSingle(); println(r.toString()); // NPE safe! 复制代码
r.toString() NullPointerException 的调用是安全的,因为如果记录不存在,a [NoDataFoundException](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/exception/NoDataFoundException.html) 会被抛出。
资源性获取
默认情况下是急切地把所有东西都取到内存中,因为这可能比JDBC默认的一直管理资源(包括嵌套集合、lobs等)对大多数应用更有用。从上面的 Iterator fetching例子中可以看出,考虑到用户甚至不能通过jOOQ访问资源(默认情况下),这往往是唯一可能不产生意外资源泄露的方法。
但这并不 总是 正确的选择,所以如果你的数据集很大的话,你可以在获取数据的同时保持开放底层JDBC资源。有2种主要方式。
强制性的
通过调用 [ResultQuery.fetchLazy()](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html#fetchLazy()) ,你就创建了一个 [Cursor<R>](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/Cursor.html) ,它包装了底层的JDBC ResultSet ,因此,应该包含在一个 try-with-resources 语句中。
try (Cursor<Record1<String>> cursor = ctx
.select(BOOK.TITLE)
.from(BOOK)
.fetchLazy()
) {
for (Record1<String> record : cursor) {
// ...
}
}
复制代码Cursor<R> 仍然扩展了 Iterable<R> ,但你也可以从它那里手动获取记录,例如:
Record record;
while ((record = cursor.fetchNext()) != null) {
// ...
}
复制代码功能性的
如果 Stream API更像你想处理的数据,只要调用 [ResultQuery.fetchStream()](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html#fetchStream()) 来代替,那么(但别忘了也要用 try-with-resources 来包装!):
try (Stream<Record1<String>> stream = ctx
.select(BOOK.TITLE)
.from(BOOK)
.fetchStream()
) {
stream.forEach(record -> {
// ...
});
}
复制代码或者,使用 Stream::map 、 Stream::reduce ,或者其他什么。遗憾的是, Stream API并不是自动关闭的。虽然这样实现API是可能的,但它的 "逃生舱",如 Stream.iterator() ,仍然会阻止自动关闭的行为(至少,除非有更多的功能被引入,如 AutoCloseableIterator ,或其他什么)。
所以,你必须用 try-with-resources 语句打破你的流畅管道。
功能性的,但不是资源性的
当然,你总是可以先调用 fetch() ,然后再调用stream,以便直接从你的内存中流转数据。如果资源性并不重要(即对性能的影响可以忽略不计,因为结果集并不大),你可以这样写:
ctx.select(BOOK.TITLE)
.from(BOOK)
.fetch()
.stream()
.forEach(record -> {
// ...
});
复制代码或者使用 Stream::map , Stream::reduce , 或其他什么方式
采集器获取
从jOOQ 3.11版本开始,无论是 [ResultQuery::collect](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html#collect(java.util.stream.Collector)) 和 [Cursor::collect](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/Cursor.html#collect(java.util.stream.Collector)) 已经被添加进来了。JDK Collector API是非常强大的。它并没有得到应有的关注(在 Stream API之外)。在我看来,应该有一个 Iterable::collect 方法,因为在任何集合上重新使用 Collector 类型是有意义的,例如:
Set<String> s = Set.of(1, 2, 3); List<String> l = s.collect(Collectors.toList()); 复制代码
为什么不呢? Collector 有点像 Stream API本身的对偶。这些操作不是以流水线的语法组成的,而是以嵌套的语法组成的。除此以外,至少对我来说,它感觉非常相似。
就jOOQ而言,它们非常强大。jOOQ提供了一些有用的开箱即用的收集器,在 [Records](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/Records.html) .让我展示一下 [Records.intoMap()](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/Records.html#intoMap()) 的例子,它有这个重载:
<K,V,R extends Record2<K,V>> Collector<R,?,Map<K,V>> intoMap() 复制代码
这里有趣的一点是,它捕获了一个 Record2 类型的类型,作为结果映射的键和值类型。一个简单的通用技巧,以确保它只在你正好投射2列的情况下工作,比如说:
Map<Integer, String> books = ctx.select(BOOK.ID, BOOK.TITLE) .from(BOOK) .collect(Records.intoMap()); 复制代码
这完全是类型安全的。你不能投射3列,或者由于所有这些泛型而投射错误的列类型。这比直接在 ResultQuery API上提供的等价物更方便,在那里你必须重复投影列的表达式:
Map<Integer, String> books = ctx.select(BOOK.ID, BOOK.TITLE) .from(BOOK) .fetchMap(BOOK.ID, BOOK.TITLE); 复制代码
通过 ResultQuery::collect 和 Cursor::collect API,你可以使用任何任意的收集器,包括你自己的收集器,这真的是非常强大的!这也是为什么你可以使用 。另外,它还消除了对中间的 Result 数据结构的需要,所以它不必把所有的东西都取到内存中(当然,除非你的 Collector 反正是这样做)。
收集器在收集 MULTISET 嵌套集合时特别有用。这里已经给出了一个例子, 一个嵌套的集合也被映射到这样的 Map<K, V> 。
反应式获取
从jOOQ 3.15开始,R2DBC得到了支持 。这意味着 [ResultQuery<R>](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/ResultQuery.html) 现在也是一个反应式流 Publisher<R> (同时支持 reactive-streams API和JDK 9 Flow API,以提高互操作性)。
所以,只要选择你最喜欢的反应式流API,比如reactor,然后像这样反应式地流取jOOQ的结果集:
Flux<Record1<String>> flux = Flux.from(ctx
.select(BOOK.TITLE)
.from(BOOK)
);
复制代码许多获取
最后但并非最不重要的是,在极少数情况下,你的查询会产生一个以上的结果集。这在SQL Server和相关的RDBMS中曾经很流行,存储过程可以产生游标。MySQL和Oracle也有这个功能。比如说:
Results results = ctx.fetch("sp_help");
for (Result<?> result : results) {
for (Record record : result) {
// ...
}
}
复制代码标准的 foreach 循环只会迭代结果,但你也可以使用下面的方法访问交错的行数 [Results.resultsOrRows()](https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/Results.html#resultsOrRows()) 如果你对这个也感兴趣的话。
总结
方便和开发者的用户体验是jOOQ的API设计的核心。像任何好的集合API一样,jOOQ提供了各种可组合的基元,允许更有效地将SQL整合到你的应用程序中。
SQL只是对数据结构的描述。jOOQ帮助在JVM上以一种类型安全的方式描述该数据结构。以同样类型安全的方式进行进一步处理是很自然的,就像我们习惯于从JDK自己的集合API或第三方(如 jOOλ 、 vavr 、 streamex 等)获得的那样。
边栏推荐
- MindSpore:【JupyterLab】查看数据时报错
- 【科普】无线电波怎样传送信息?
- Start background services across processes
- 【Node实现数据加密】
- Correct pose of Vulkan open feature
- 055 c# print
- SimpleOSS third-party library libcurl and engine libcurl error solution
- Talking about Contrastive Learning (Contrastive Learning) the first bullet
- ImportError: attempted relative import with no known parent package
- iPhone真是十三香?两代产品完全对比,或许上一代更值得买
猜你喜欢

MySQL分库分表

mysql慢查询优化

牛客刷题系列之进阶版(搜索旋转排序数组,链表内指定区间反转)

golang日志库zerolog使用记录

Frog jumping steps (recursive and non-recursive) ------- Xiaolele walks the steps

Linux下安装MySQL教程

Snowflake vs. Redshift的2022战报:两个数据平台谁更适合你?

ELK日志分析系统

【MindSpore】用coco2017训练Model_zoo上的 yolov4,迭代了两千多batch_size之后报错,大佬们帮忙看看。

The JDBC programming of the MySQL database
随机推荐
MySQL mass production of data
Perfectly Clear QuickDesk & QuickServer图像校正优化工具
Linux下安装MySQL教程
阿里面试这些微服务还不会?那还是别去了,基本等通知
【私人系列】日常PHP遇到的各种稀奇古怪的问题
MindSpore:mindspore有没有类似tf.GradientTape()用来求解梯度的?
第十七届“振兴杯”全国青年 职业技能大赛——计算机程序设计员(云计算平台与运维)参赛回顾与总结
数据库索引:索引并不是万能药
看完《二舅》,我更内耗了
MySQL数据库主从配置
MySQL slow query optimization
Centos7 install mysql8
The advanced version of the Niu Ke brushing series (team competition, sorting subsequences, inverting strings, deleting common characters, repairing pastures)
【MindSpore】多卡训练保存权重问题
LeetCode 0952. Calculate Maximum Component Size by Common Factor: Mapping / Union Search
[hbuilder] cannot run some projects, open the terminal and cannot enter commands
DCM 中间件家族迎来新成员
MindSpore:自定义dataset的tensor问题
什么是 RESTful API?
Database Tuning - Database Tuning