在使用DB时,为什么您更喜欢Java8流API而不是直接的hibernate/sql查询

在使用DB时,为什么您更喜欢Java8流API而不是直接的hibernate/sql查询,java,hibernate,java-8,java-stream,Java,Hibernate,Java 8,Java Stream,最近,我在少数几个项目中看到了大量使用流过滤对象的代码,如: library.stream() .map(book -> book.getAuthor()) .filter(author -> author.getAge() >= 50) .map(Author::getSurname) .map(String::toUpperCase) .distinct()

最近,我在少数几个项目中看到了大量使用流过滤对象的代码,如:

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()
。(免责声明:如果仅仅更改流类型将产生正确的结果,当然这取决于特定的上下文;但是,是的,这可能很容易)

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

这些都是如今人们更喜欢“流”解决方案的原因(从一般的角度来看)

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


但是,当然:当上面的调用,比如
book.getAuthor()
将进行“深入”并实际与底层数据库对话时;那么,“在一个查询中完成全部工作”可能会提高性能

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

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


第二,在进行本地筛选时,获取和传输许多记录,并将数据解组到对象中,只是为了扔掉大量记录,这是对带宽和计算资源的浪费。

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

考虑到这一点,使用streamlike API有很多原因

  • 它很时髦。Streams和Lambda对于大多数java开发人员来说还是相当新的,所以他们在使用它时感觉很酷

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

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

  • 他们更喜欢阅读命令式代码。可能流中的处理不能[轻松地/由作者]用SQL完成。因此,替代方案不是SQL vs Java(或您正在使用的任何语言),而是命令式Java或“函数式”Java。后者通常读起来更好

  • 因此,有充分的理由使用这样的API

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

    除非针对特定场景进行测量和证明,否则可能是好的,也可能是坏的。通常希望对数据库执行此类查询的原因是(除其他外):

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

    可以为数据库中的查询编制索引(使其更快)


    另一方面,如果您的数据很小,那么使用
    的方法是有效的。编写这样的流管道是非常可读的(只要你把流说得足够好)。

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


    现在,对于读取和报告,另一方面(考虑到我们在这里谈论的是DB),SQL查询可能会更好,因为在这两者之间不会有任何框架,您将能够根据调用此查询的数据库而不是您选择的框架来调整查询性能,这在某种程度上为如何进行调优提供了更大的灵活性。

    好吧,理想情况下,您的问题应该是-在数据库中执行缩减/过滤操作,还是使用流获取所有记录并在Java中执行它更好?

    答案并不直截了当,任何给出“具体”答案的统计数据都不会推广到所有情况

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

    一个我会