前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Java Streams(流)查询数据库

使用Java Streams(流)查询数据库

作者头像
程序你好
发布2018-10-18 15:38:23
2.8K0
发布2018-10-18 15:38:23
举报
文章被收录于专栏:程序你好程序你好

在本文中,您将了解如何编写纯Java应用程序,这些应用程序能够使用来自现有数据库的数据,而无需编写一行SQL(或类似的语言,如HQL),也无需花费大量时间将所有内容组合在一起。在应用程序准备好之后,您将学习如何使用 in-JVM-acceleration(仅添加两行代码)加速超过1,000倍的延迟性能。

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

您还将发现,数据访问性能可以通过直接从RAM运行流的in-JVM-memory技术显著提高。

示例数据库

我们将使用来自MySQL的示例数据库Sakila。它有电影、演员、类别等表格,可以免费下载 here.

步骤 1: 连接到数据库

我们将开始配置pom。您可以在这里找到使用Speedment Initializer的xml文件,点击 here下载. 您将得到带有主文件夹的项目。java文件自动生成。

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

接下来,执行命令行:

代码语言:javascript
复制
mvn speedment:tool

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

步骤 2: 生产代码

从数据库加载模式数据之后,可以通过按“Generate”按钮生成完整的Java领域模型。

这只需要一两秒钟。

步骤 3: Write the Application Code

与步骤2中的域模型一起,将自动生成Speedment实例的构建器。打开main.java文件,并将main()方法中的代码替换为以下代码片段:

代码语言:javascript
复制
SakilaApplication app = new SakilaApplicationBuilder()
代码语言:javascript
复制
    .withPassword("sakila-password") // Replace with your own password
代码语言:javascript
复制
    .build();

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

代码语言:javascript
复制
// Obtains a FilmManager that allows us to
代码语言:javascript
复制
// work with the "film" table
代码语言:javascript
复制
FilmManager films = app.getOrThrow(FilmManager.class);
代码语言:javascript
复制
代码语言:javascript
复制
// Create a stream of all films and print
代码语言:javascript
复制
// each and every film
代码语言:javascript
复制
films.stream()
代码语言:javascript
复制
    .forEach(System.out::println);

是不是很简单?

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

代码语言:javascript
复制
SakilaApplication app = new SakilaApplicationBuilder()
代码语言:javascript
复制
    .withPassword("sakila-password")
代码语言:javascript
复制
    .withLogging(ApplicationBuilder.LogType.STREAM)
代码语言:javascript
复制
    .build();

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

代码语言:javascript
复制
SELECT
代码语言:javascript
复制
    `film_id`,`title`,`description`,`release_year`,
代码语言:javascript
复制
    `language_id`,`original_language_id`,`rental_duration`,`rental_rate`,
代码语言:javascript
复制
    `length`,`replacement_cost`,`rating`,`special_features`,`last_update`
代码语言:javascript
复制
 FROM
代码语言:javascript
复制
     `sakila`.`film`,
代码语言:javascript
复制
values:[]

呈现的SQL代码可能因所选择的数据库类型而异(例如MySQL、MariaDB、PostgreSQL、Oracle、MS SQL Server、DB2、AS400等)。这些变化是自动的。

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

代码语言:javascript
复制
FilmImpl { filmId = 1, title = ACADEMY DINOSAUR, ..., length = 86, ... }
代码语言:javascript
复制
FilmImpl { filmId = 2, title = ACE GOLDFINGER, ..., length = 48, ...}
代码语言:javascript
复制
FilmImpl { filmId = 3, title = ADAPTATION HOLES, ..., length = 50, ...}
代码语言:javascript
复制
...

步骤 4: 过滤

高速流支持包括过滤器在内的所有流操作。假设我们只过滤那些超过60分钟的电影。这可以通过向我们的应用程序添加这一行代码来实现:

代码语言:javascript
复制
films.stream()
代码语言:javascript
复制
    .filter(Film.LENGTH.greaterThan(60))
代码语言:javascript
复制
    .forEach(System.out::println);

SQL:

代码语言:javascript
复制
SELECT
代码语言:javascript
复制
    `film_id`,`title`,`description`,`release_year`,
代码语言:javascript
复制
    `language_id`,`original_language_id`,`rental_duration`,`rental_rate`,
代码语言:javascript
复制
     `length`,`replacement_cost`,`rating`,`special_features`,
代码语言:javascript
复制
    `last_update`
代码语言:javascript
复制
FROM
代码语言:javascript
复制
    `sakila`.`film`
代码语言:javascript
复制
WHERE
代码语言:javascript
复制
    (`length` > ?),
代码语言:javascript
复制
 values:[60]

生成的输出:

代码语言:javascript
复制
FilmImpl { filmId = 1, title = ACADEMY DINOSAUR, ..., length = 86, ... }
代码语言:javascript
复制
FilmImpl { filmId = 4, title = AFFAIR PREJUDICE, ..., length = 117, ...}
代码语言:javascript
复制
FilmImpl { filmId = 5, title = AFRICAN EGG, ... length = 130, ...}

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

代码语言:javascript
复制
films.stream()
代码语言:javascript
复制
    .filter(
代码语言:javascript
复制
        Film.LENGTH.greaterThan(60).or(Film.LENGTH.lessThan(30))
代码语言:javascript
复制
    )
代码语言:javascript
复制
    .forEach(System.out::println);

这将返回所有小于30分钟或大于1小时的影片。检查您的日志文件,您将看到这个流也被呈现给SQL。

Step 5:控制顺序

默认情况下,流中元素出现的顺序是未定义的。要定义特定的顺序,您可以对流应用一个sort()操作,如下所示:

films.stream()

代码语言:javascript
复制
    .filter(Film.LENGTH.greaterThan(60))
代码语言:javascript
复制
    .sorted(Film.TITLE)
代码语言:javascript
复制
    .forEach(System.out::println);

Rendered SQL:

代码语言:javascript
复制
SELECT
代码语言:javascript
复制
    `film_id`,`title`,`description`,`release_year`,
代码语言:javascript
复制
    `language_id`,`original_language_id`,`rental_duration`,`rental_rate`,
代码语言:javascript
复制
    `length`,`replacement_cost`,`rating`,`special_features`,
代码语言:javascript
复制
    `last_update`
代码语言:javascript
复制
FROM
代码语言:javascript
复制
    `sakila`.`film`
代码语言:javascript
复制
WHERE
代码语言:javascript
复制
    (`length` > ?)
代码语言:javascript
复制
ORDER BY
代码语言:javascript
复制
    `length` ASC,
代码语言:javascript
复制
values:[60]

输出:

代码语言:javascript
复制
FilmImpl { filmId = 77, title = BIRDS PERDITION,..., length = 61,...}
代码语言:javascript
复制
FilmImpl { filmId = 106, title = BULWORTH COMMANDMENTS,..., length = 61,}
代码语言:javascript
复制
FilmImpl { filmId = 114, title = CAMELOT VACATION,..., length = 61,..}
代码语言:javascript
复制
...

您还可以组合多个排序器来定义主顺序、次顺序等等。

代码语言:javascript
复制
films.stream()
代码语言:javascript
复制
    .filter(Film.LENGTH.greaterThan(60))
代码语言:javascript
复制
    .sorted(Film.LENGTH.thenComparing(Film.TITLE.reversed()))
代码语言:javascript
复制
    .forEach(System.out::println);

将按长度顺序(升序)和标题顺序(降序)对电影元素进行排序。您可以组合任意数量的字段。

NB:如果要按升序组合两个或多个字段,应该使用字段的method.comparator()。

I.e. sorted(Film.LENGTH.thenComparing(Film.TITLE.comparator())) rather than just sorted(Film.LENGTH.thenComparing(Film.TITLE))

步骤 6: 分页和避免大对象块

通常,人们希望对结果进行分页,以避免使用不必要的大型对象块。假设我们希望每页看到50个记录,我们可以编写以下通用方法:

代码语言:javascript
复制
private static final int PAGE_SIZE = 50;
代码语言:javascript
复制
代码语言:javascript
复制
public static <T> Stream<T> page(
代码语言:javascript
复制
    Manager<T> manager,
代码语言:javascript
复制
    Predicate<? super T> predicate,
代码语言:javascript
复制
    Comparator<? super T> comparator,
代码语言:javascript
复制
    int pageNo
代码语言:javascript
复制
) {
代码语言:javascript
复制
    return manager.stream()
代码语言:javascript
复制
        .filter(predicate)
代码语言:javascript
复制
        .sorted(comparator)
代码语言:javascript
复制
        .skip(pageNo * PAGE_SIZE)
代码语言:javascript
复制
        .limit(PAGE_SIZE);
代码语言:javascript
复制
}

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

例如,调用:

代码语言:javascript
复制
page(films, Film.LENGTH.greaterThan(60), Film.TITLE, 3)

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

Rendered SQL:

代码语言:javascript
复制
SELECT
代码语言:javascript
复制
    `film_id`,`title`,`description`,`release_year`,
代码语言:javascript
复制
    `language_id`,`original_language_id`,`rental_duration`,`rental_rate`,
代码语言:javascript
复制
    `length`,`replacement_cost`,`rating`,`special_features`,
代码语言:javascript
复制
    `last_update`
代码语言:javascript
复制
FROM
代码语言:javascript
复制
    `sakila`.`film`
代码语言:javascript
复制
WHERE
代码语言:javascript
复制
    (`length` > ?)
代码语言:javascript
复制
ORDER BY
代码语言:javascript
复制
     `title` ASC
代码语言:javascript
复制
LIMIT ? OFFSET ?,
代码语言:javascript
复制
values:[60, 50, 150]

Generated output:

代码语言:javascript
复制
FilmImpl { filmId = 165, title = COLDBLOODED DARLING, ... length = 70,...}
代码语言:javascript
复制
FilmImpl { filmId = 166, title = COLOR PHILADELPHIA, ..., length = 149... }
代码语言:javascript
复制
FilmImpl { filmId = 167, title = COMA HEAD, ... length = 109,...}
代码语言:javascript
复制
...

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

步骤 7: In-JVM-Memory 加速

由于在初始化器中使用了标准配置,所以pom.xml中启用了in - jvm -memory加速文件。要在应用程序中激活加速,只需修改初始化代码如下:

代码语言:javascript
复制
SakilaApplication app = new SakilaApplicationBuilder()
代码语言:javascript
复制
    .withPassword("sakila-password")
代码语言:javascript
复制
    .withBundle(InMemoryBundle.class)
代码语言:javascript
复制
    .build();
代码语言:javascript
复制
代码语言:javascript
复制
    // Load data from the database into an in-memory snapshot
代码语言:javascript
复制
    app.getOrThrow(DataStoreComponent.class).load();

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

在我的笔记本电脑(Mac Pro,15英寸,2015年中期,16 GB,i7 2.2 GHz),查询延迟降低了流的因素超过1000,我计算匹配过滤和排序的电影流相比,针对标准安装运行的MySQL数据库(版本5.7.16)在我的本地机器上运行。

总结

在本文中,您已经了解了使用纯Java流查询现有数据库是多么容易。您还看到了如何使用in-JVM-memory stream技术加速对数据的访问。Sakila数据库和Speedment都是免费下载和使用的,所以自己试试吧。

请关注公众号:程序你好

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序你好 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 示例数据库
  • 步骤 1: 连接到数据库
  • 步骤 2: 生产代码
  • 步骤 3: Write the Application Code
  • 步骤 4: 过滤
  • Step 5:控制顺序
  • 步骤 6: 分页和避免大对象块
  • 步骤 7: In-JVM-Memory 加速
  • 总结
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档