繁体   English   中英

在使用数据库时,为什么要使用Java 8 Stream API而不是直接的hibernate / sql查询

[英]Why would you prefer Java 8 Stream API instead of direct hibernate/sql queries when working with the DB

最近我在很少的项目中看到很多代码使用流来过滤对象,例如:

library.stream()
          .map(book -> book.getAuthor())
          .filter(author -> author.getAge() >= 50)
          .map(Author::getSurname)
          .map(String::toUpperCase)
          .distinct()
          .limit(15)
          .collect(toList()));

使用它而不是直接HQL / SQL查询到数据库返回已经过滤的结果是否有任何好处。

第二种方法不是更快吗?

如果数据最初来自数据库,最好在数据库中进行过滤,而不是获取所有内容并在本地过滤。

首先,数据库管理系统擅长过滤,它是主要工作的一部分,因此针对它进行了优化。 也可以使用索引加速过滤。

其次,获取和传输许多记录并将数据解组成对象只是为了在进行本地过滤时丢掉大量数据而浪费带宽和计算资源。

乍一看:溪流可以平行运行; 只需更改代码即可使用parallelStream() (免责声明:当然,如果只改变流类型将导致正确的结果,它取决于具体的上下文;但是,它可以很容易)。

然后:流“邀请”使用lambda表达式。 而这些又导致使用invoke_dynamic字节码指令; 与“老派”编写此类代码相比,有时会获得性能优势。 (并澄清误解:invoke_dynamic是lambda的属性,而不是stream!)

这些将是现在更喜欢“流”解决方案的理由(从一般观点来看)。

除此之外:它真的取决于...让我们来看看您的示例输入。 这看起来像处理已经驻留在内存中的普通Java POJO,在某种集合中。 直接在内存中处理这些对象肯定比去一些进程外数据库在那里工作更快!

但是,当然:当上述调用时,例如book.getAuthor()将进行“深入研究”并实际与底层数据库交谈; 然后很有可能“在一个查询中完成整个事情”会给你带来更好的表现。

首先要意识到,您无法从这段代码中分辨出针对数据库发出的语句。 很可能,收集了所有过滤,限制和映射,并且在调用collect所有这些信息都用于构造匹配的SQL语句(或使用的任何查询语言)并发送到数据库。

考虑到这一点,使用流式API的原因有很多。

  1. 这是时髦的。 Streams和lambdas对于大多数Java开发人员来说仍然是一个新手,所以当他们使用它时他们感觉很酷。

  2. 如果使用第一段中的内容,它实际上创建了一个很好的DSL来构造您的查询语句。 Scalas Slick.Net LINQ我知道的早期例子,虽然我假设有人在我出生之前就已经在LISP中构建了类似的东西。

  3. 流可能是反应流并封装非阻塞API。 虽然这些API非常好,因为它们不会强迫您在等待结果时阻止线程等资源。 使用它们需要大量的回调或使用更好的基于流的API来处理结果。

  4. 他们更好地阅读命令式代码。 也许在流中完成的处理不能[轻松/由作者]完成SQL。 因此,替代方案不是SQL与Java(或您正在使用的语言),而是命令式Java或“功能”Java。 后者经常读得更好。

所以有充分的理由使用这样的API。

尽管如此:在几乎所有情况下,当您可以将其卸载到数据库时,在应用程序中进行任何排序/过滤等都是个坏主意。 我目前唯一能想到的例外是你可以跳过整个往返数据库,因为你已经在本地获得了结果(例如在缓存中)。

除非针对特定情况进行测量和验证 ,否则可能是好的或同样糟糕的。 通常希望对数据库进行这类查询的原因是(除其他外):

DB可以处理比java进程更大的数据

可以索引数据库中的查询(使它们更快)

另一方面,如果您的数据很小,那么使用Stream就可以了。 编写这样的Stream管道非常易读 (一旦你 Streams足够好)。

那么,理想情况下你的问题应该是 - 在数据库中进行缩减/过滤操作或获取所有记录并使用Streams在Java中执行它是否更好?

答案并不简单,任何给出“具体”答案的统计数据都不会推广到所有情况。

您正在谈论的操作最好在DB本身中完成,因为这是DB的设计目标,非常快速地处理数据 当然,通常在关系数据库的情况下,会有一些“簿记和锁定”用于确保独立事务不会最终导致数据不一致,但即使如此,DB在过滤方面也做得非常好数据,尤其是大型数据集。

如果您需要从相同数据中过滤不同的功能,我倾向于使用Java代码而不是数据库过滤数据。 例如,现在您只获得作者的姓氏。 如果你想获得作者所写的所有书籍,作者的年龄,作者的孩子,出生地等等。那么从数据库中只获得一个“只读”副本并使用并行流来获取不同的信息是有意义的来自相同的数据集。

Hibernate和其他ORM通常对于编写实体而不是读取更有用,因为它们允许开发人员将特定写入的排序卸载到几乎永远不会“出错”的框架。

现在,对于阅读和报告,另一方面(考虑到我们在这里谈论DB),SQL查询可能会更好,因为中间不会有任何框架,您将能够调整查询性能将调用此查询的数据库而不是您选择的框架,这为调整的完成方式提供了更大的灵活性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM