使用Java Streams查询数据库

本文介绍如何使用Java Streams处理数据库数据,无需编写SQL,利用Speedment工具生成代码,自动转换Java Streams为SQL,以及如何通过In-JVM内存加速提高数据访问性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用Java Streams查询数据库
在本文中,您将学习如何编写能够处理现有数据库中的数据的纯Java应用程序,而无需编写单行SQL(或类似的HQL语言),也无需花费数小时将所有内容放在一起。在您的应用程序准备就绪后,您将学习如何通过添加两行代码,使用in-JVM加速来加速延迟性能超过1,000。

在本文中,我们将使用Speedment,它是一个Java流ORM,可以直接从数据库模式生成代码,并且可以自动将Java Streams直接呈现给SQL,允许您使用纯Java编写代码。

您还将发现,通过JVM内存技术可以显着提高数据访问性能,其中Streams直接从RAM运行。

示例数据库
我们将使用名为Sakila的MySQL示例数据库。它有表格,电影,演员,类别等,可以在这里免费下载。

第1步:连接到您的数据库
我们将pom.xml使用您可以在此处找到的Speedment Initializer 开始配置文件。按“下载”,您将获得项目文件夹,其中Main.java包含自动生成的文件。

接下来,解压缩项目文件夹zip文件,打开命令行,然后转到解压缩文件夹(pom.xml文件所在的位置)。

然后,输入以下命令:

mvn speedment:工具
这将启动Speedment工具并提示您输入许可证密钥。选择“开始免费”,您将自动获得免费许可。现在您可以连接到数据库并开始使用:

第2步:生成代码
从数据库加载架构数据后,可以通过按“生成”按钮生成完整的Java域模型。

这只需要一两秒钟。

第3步:编写应用程序代码
与步骤2中的域模型一起,自动生成Speedment实例的构建器。打开Main.java文件并使用以下代码替换main()方法中的代码:

SakilaApplication  app  =  new  SakilaApplicationBuilder()
    。withPassword(“sakila-password”)//替换为您自己的密码
    。build();

接下来,我们将编写一个将打印出所有电影的应用程序。不可否认,这是一个小应用程序,但我们将在本文的过程中对其进行改进。

//获得允许我们使用的FilmManager
//使用“电影”表
FilmManager  电影 =  app。getOrThrow(FilmManager。类);
//创建所有电影的流和打印
//每部电影
电影。流()
    。的forEach(系统。出来 :: 的println);

不是那么简单吗?

运行时,Java流将自动呈现给SQL。为了实际查看呈现的SQL代码,请修改我们的应用程序构建器并使用STREAM日志类型启用日志记录:

SakilaApplication  app  =  new  SakilaApplicationBuilder()
    。withPassword(“sakila-password”)
    。withLogging(ApplicationBuilder。日志类型。STREAM)
    。build();

这是运行应用程序时SQL代码的样子:

选择
film_idtitledescriptionrelease_year
language_idoriginal_language_idrental_durationrental_rate
lengthreplacement_costratingspecial_featureslast_update

sakila.film
值:[]
呈现的SQL代码可能因您选择的数据库类型而异(例如MySQL,MariaDB,PostgreSQL,Oracle,MS SQL Server,DB2,AS400等)。这些变化是自动的。

上面的代码将产生以下输出(为简洁起见缩短):

FilmImpl {filmId = 1,title = ACADEMY DINOSAUR,...,length = 86,...}
FilmImpl {filmId = 2,title = ACE GOLDFINGER,...,length = 48,...}
FilmImpl {filmId = 3,title = ADAPTATION HOLES,...,length = 50,...}
...

第4步:使用过滤器
Speedment流支持所有Stream操作,包括过滤器。假设我们只想过滤那些超过60分钟的电影。这可以通过在我们的应用程序中添加以下代码来实现:

films.stream()
.filter(Film.LENGTH.greaterThan(60))
.forEach(的System.out ::的println);
渲染SQL:

选择
film_idtitledescriptionrelease_year
language_idoriginal_language_idrental_durationrental_rate
lengthreplacement_costratingspecial_features
last_update

sakila.film
哪里
length>?),
价值观:[ 60 ]
生成的输出:


FilmImpl {filmId = 1,title = ACADEMY DINOSAUR,...,length = 86,...}
FilmImpl {filmId = 4,title = AFFAIR PREJUDICE,...,length = 117,...}
FilmImpl {filmId = 5,title = AFRICAN EGG,... length = 130,...}

可以组合过滤器以创建更复杂的表达式,如下所示:

电影。流()
    。过滤器(
        电影。长度。大唐(60)。或(电影。长度。较少(30))
    )
    。的forEach(系统。出来 :: 的println);

这将返回所有短于30分钟或超过一小时的电影。检查您的日志文件,您将看到此Stream也呈现给SQL。

第5步:定义元素的顺序
默认情况下,元素在流中的显示顺序是未定义的。要定义特定订单,请将sorted()操作应用于如下所示的流:


电影。流()
    。过滤器(电影。LENGTH。GREATERTHAN(60))
    。排序(电影。标题)
    。的forEach(系统。出来 :: 的println);

渲染SQL:

选择 
    `film_id`,`title`,`description`,`release_year`,
    `language_id`,`original_language_id`,`rental_duration`,`rental_rate`,
    `length`,`replacement_cost`,`rating`,`special_features`,
    `last_update` 
从 
    `sakila`.`film` 
哪里 
    (`长度`>?) 
ORDER  BY 
    `长度` ASC,
价值观:[ 60 ]

生成的输出:

FilmImpl {filmId = 77,title = BIRDS PERDITION,...,length = 61,...}
FilmImpl {filmId = 106,title = BULWORTH COMMANDMENTS,...,length = 61,}
FilmImpl {filmId = 114,title = CAMELOT VACATION,...,length = 61,..}
...

您还可以组合多个分拣机来定义主要订单,次要订单等。


电影。流()
    。过滤器(电影。LENGTH。GREATERTHAN(60))
    。排序(电影。长度。然后比较(电影。标题。反转()))
    。的forEach(系统。出来 :: 的println);

这将按照LENGTH顺序(升序)然后按TITLE顺序(降序)对电影元素进行排序。您可以撰写任意数量的字段。

注意:如果您按升序组成两个或多个字段,则应使用字段的方法.comparator()。也就是说,sorted(Film.LENGTH.thenComparing(Film.TITLE.comparator()))而不仅仅是sorted(Film.LENGTH.thenComparing(Film.TITLE))

第6步:页面并避免大对象块
通常,人们想要分页结果以避免使用不必要的大对象块。假设我们希望每页看到50个元素,我们可以编写以下通用方法:

private  static  final  int  PAGE_SIZE  =  50 ;
public  static  < T >  Stream < T >  页面(
    经理< T >  经理,
    谓词<? 超级 T >  谓词,
    比较器<? 超级 T >  比较器,
    int  pageNo
){
    回归 经理。流()
        。过滤器(谓词)
        。排序(比较器)
        。跳过(pageNo  *  PAGE_SIZE)
        。限制(PAGE_SIZE);
}

此实用程序方法可以使用ANY过滤器对ANY表进行分页,并按任意顺序对其进行排序。

例如,调用:


page(电影,电影.LENGTH.greaterThan(60),Film.TITLE,3)

将返回超过60分钟的电影流,并按照显示第三页的标题排序(即跳过150部电影并显示以下50部电影)。

渲染SQL:


选择 
    `film_id`,`title`,`description`,`release_year`,
    `language_id`,`original_language_id`,`rental_duration`,`rental_rate`,
    `length`,`replacement_cost`,`rating`,`special_features`,
    `last_update` 
从 
    `sakila`.`film` 
哪里
    (`长度`>?) 
ORDER  BY
     `title` ASC 
限制 ?OFFSET ?,
值:[ 60,50,150 ]

生成的输出:

FilmImpl {filmId = 165,title = COLDBLOODED DARLING,... length = 70,...}
FilmImpl {filmId = 166,title = COLOR PHILADELPHIA,...,length = 149 ...}
FilmImpl {filmId = 167,title = COMA HEAD,... length = 109,...}
...

同样,如果我们使用了另一种数据库类型,那么SQL代码会略有不同。

第7步:In-JVM内存加速
由于您在初始化程序中使用了标准配置,因此在您的pom.xml文件中启用了In-JVM内存加速。要激活应用程序中的加速,只需修改初始化代码,如下所示:

SakilaApplication  app  =  new  SakilaApplicationBuilder()
    。withPassword(“sakila-password”)
    。withBundle(InMemoryBundle。类)
    。build();
    //将数据库中的数据加载到内存中快照中
    app。getOrThrow(DataStoreComponent。类)。load();

现在,不是呈现SQL查询,而是直接从RAM提供表流。内存索引也会加速过滤,排序和跳过。内存表和索引都存储在堆外,因此它们不会导致垃圾收集复杂性。

在我的笔记本电脑(Mac Book Pro,15英寸,2015年中期,16 GB,i7 2.2 GHz)上,对于我计算与过滤器和排序流相匹配的影片的流,查询延迟减少了1,000倍以上反对在我的本地计算机上运行的MySQL数据库(版本5.7.16)的标准安装。

概要
在本文中,您了解了使用纯Java流查询现有数据库是多么容易。您还了解了如何使用in-JVM内存流技术加速对数据的访问。Sakila数据库和Speedment都可以免费下载和使用,所以请亲自试试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值