接触一个新工具的时候,刚开始要克服的***障碍就是如何让你自己先尝试做出一个小东西来。现在你也许对 Java 8 中新的 Stream API 的运作方式在理解上比较自信,但你也许并没用它来进行过数据库查询操作。为了帮助你开始使用 Stream API 来对 SQL 数据库进行创建、修改和读取操作, 我已经在这个快速开始的教程中把它们整合到了一起。希望它能帮助你提升对流式API的使用水平!
背景
Speedment 是一个开放源代码的工具集,它可以被用来生成 Java 实体,并且能将我们同数据库的通信过程管理起来。你可以利用一个图形工具连接到数据库并生成出一套完整的 ORM 框架代码来表示域模型。但是 Speedment 不单单只是一个代码生成器而已,它还是一个能插入应用程序中的运行时程序,这样就有可能将你的 Java 8 流式代码翻译成优化过的SQL查询。这也是我将会在本文中专门讲述的一个部分。
生成代码
要在一个 Maven 工程中开始使用 Speedment,需要你将下面几行代码添加到你的 pom.xml 文件中。在本例中,我使用的是 MySQL,而你也可以选择使用 PostgreSQL 或者 MariaDB。面向于像Oracle这样的专有数据库可用于企业级客户。
Pom.xml
- <properties>
- <speedment.version>3.0.1</speedment.version>
- <db.groupId>mysql</db.groupId>
- <db.artifactId>mysql-connector-java</db.artifactId>
- <db.version>5.1.39</db.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>com.speedment</groupId>
- <artifactId>runtime</artifactId>
- <version>${speedment.version}</version>
- <type>pom</type>
- </dependency>
- <dependency>
- <groupId>${db.groupId}</groupId>
- <artifactId>${db.artifactId}</artifactId>
- <version>${db.version}</version>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>com.speedment</groupId>
- <artifactId>speedment-maven-plugin</artifactId>
- <version>${speedment.version}</version>
- <dependencies>
- <dependency>
- <groupId>${db.groupId}</groupId>
- <artifactId>${db.artifactId}</artifactId>
- <version>${db.version}</version>
- </dependency>
- </dependencies>
- </plugin>
- </plugins>
- </build>
现在你可以访问到许多新的 Maven 资源库,它们能让你更加轻松的使用这个工具包。要启动 Speedment UI, 执行如下命令:
- mvn speedment:tool
这样就会有一个过程引导你连接到数据库并对代码生成进行配置。一开始最简单的方法就是用默认的设置先跑起来再说。当你按下生成按钮“Generate,” Speedment 就会对你的数据库元数据进行分析,然后在你的工程中添加像实体和实体管理器这样的类。
初始化 Speedment
当你的域模型生成好了以后,Speedment 的设置就容易了。创建一个新的 Main.java 文件然后添加如下几行代码。你看到的类都是生成的,因此它们的命名都是根据数据库模式、表以及列的名称来决定的。
Main.java
- public class Main {
- public static void main(String... param) {
- final HaresApplication app = new HaresApplicationBuilder()
- .withPassword("password")
- .build();
- }
- }
上面的代码创建了一个新的应用程序实体,它使用了一种生成的构造器模式。构造器是的对任何运行时配置细节的设置成为可能,例如数据库的密码。
当我们有了一个应用实体,就可以用它来访问生成的实体管理器了。在这里,我的数据库中有了四个表; “hare”, “carrot”, “human”, 以及 “friend”. (你可以在这里找到完整的数据库定义)。
- final CarrotManager carrots = app.getOrThrow(CarrotManager.class);
- final HareManager hares = app.getOrThrow(HareManager.class);
- final HumanManager humans = app.getOrThrow(HumanManager.class);
- final FriendManager hares = app.getOrThrow(FriendManager.class);
现在这些实体管理器可以被用来执行所有的CRUD操作了。
创建实体
创建实体的方式非常直接。我们就使用实体生成的实现,把列的值设置好然后持久化到数据源就可以了。
- hares.persist(
- new HareImpl()
- .setName("Harry")
- .setColor("Gray")
- .setAge(8)
- );
persist 方法会返回一个 (潜在的) Hare 新实例,里面像“id”这种自动生成键已经设置好了。如果我们想在持久化之后继续使用 Harry, 那就可以使用 persist 方法返回的这个:
- final Hare harry = hares.persist(
- new HareImpl()
- .setName("Harry")
- .setColor("Gray")
- .setAge(8)
- );
如果持久化操作失败了,例如如果有一个外键违反了唯一性约束,就会有一个 SpeedmentException 抛出。我们应该对此进行检查,如果有默写东西会阻止我们对这条 hare 记录进行持久化,就应该显示一条错误信息。
- try {
- final Hare harry = hares.persist(
- new HareImpl()
- .setName("Harry")
- .setColor("Gray")
- .setAge(8)
- );
- } catch (final SpeedmentException ex) {
- System.err.println(ex.getMessage());
- return;
- }
读取实体
Speedment 运行时中最酷的功能特性就是能够使用 Java 8 的 Stream API对数据库中的数据进行流式操作。“为什么这样做会很酷呢?” 你可能会这样问你自己。“如今甚至Hibernate 都已经支持流式操作了!”这就是回答。
使用 Speedment 流式操作最美好的事情就是它们把构建流的中间和终止动作都考虑进去了。这就意味着如果你在流已经被创建之后添加一个过滤器进去,那么在构建 SQL 语句时这个过滤器也会被考虑进去。
下面是一个示例,我们想要计算数据库中 hare 记录的总数。
- final long haresTotal = hares.stream().count();
- System.out.format("There are %d hares in total.%n", haresTotal);
这段代码将会生成的SQL查询如下:
- SELECT COUNT(id) FROM hares.hare;
这里的终止操作就是 .count() ,因此 Speedment 就知道是要创建一个 SELECT COUNT(…) 语句。它也知道 “hare”表的主键是“id”这个列,如此就有可能将发送给数据库的整个语句 减少到这个样子。
更加复杂的示例可能就是找出名称以 “rry” 并且年龄大于等于 5 的兔子的数量。这个可以这样写:
- final long complexTotal = hares.stream()
- .filter(Hare.NAME.endsWith("rry"))
- .filter(Hare.AGE.greaterOrEqual(5))
- .count();
我们使用由 Speedment 为我们生成的位于构建器来定义过滤器。这使得我们以编程的方式对流进行分析并且将其简化到如下这样一条SQL语句成为可能:
- SELECT COUNT(id) FROM hares.hare
- WHERE hare.name LIKE CONCAT("%", ?)
- AND hare.age >= 5;
如果我们添加了一个 Speedment 不可以对流进行优化的操作, 它就会像一般的 Java 8 流那被处理。我们永远都不会限制生成的位于构建器的使用,它能是流式操作更加的高效。
- final long inefficientTotal = hares.stream()
- .filter(h -> h.getName().hashCode() == 52)
- .count();
上述代码会产生一条如下极其低效的语句,但是它仍然可以跑起来。
- SELECT id,name,color,age FROM hares.hare;
更新实体
更新存在的实体和读取以及持久化实体非常相似。在我们调用update()方法之前,对实体本地拷贝的改变,不会影响数据库内容。
下面,我们拿到之前使用Hare创建的Harry,并将他的颜色变为棕色:
- harry.setColor("brown");
- final Hare updatedHarry = hares.update(harry);
如果更新被接受了,那么管理器会返回hare的一个新的拷贝,因为我们在后面会继续使用这个实例。就想做“创建”的例子中,更新可能会失败。也许颜色被定义为“值唯一”,棕色已经存在于hare中。那样的话,会抛出一个SpeedmentException异常.
我们也可以通过合并多个实体到一个流中来同时更新他们。加入我们想将所有名字为Harry的hare变为棕色,我们可以这样做:
- hares.stream()
- .filter(Hare.NAME.equal("Harry"))
- .map(Hare.COLOR.setTo("Brown"))
- .forEach(hares.updater()); // 更新流中存在的元素
我们还应该使用try-catch语句来确保在运行过程中有失败发生时警告用户。
- try {
- hares.stream()
- .filter(Hare.NAME.equal("Harry"))
- .map(Hare.COLOR.setTo("Brown"))
- .forEach(hares.updater());
- } catch (final SpeedmentException ex) {
- System.err.println(ex.getMessage());
- return;
- }
实体删除
我们需要知道的***一个 CRUD 操作就是从数据库中删除实体。这个操作几乎和“更新”操作时等同的。假如说我们要把年龄超过10岁的兔子的记录都删除,就要这样做:
- try {
- hares.stream()
- .filter(Hare.AGE.greaterThan(10))
- .forEach(hares.remover()); // Removes remaining hares
- } catch (final SpeedmentException ex) {
- System.err.println(ex.getMessage());
- return;
- }
总结
通过阅读本文你已经了解了如何在一个 Maven 工程中对 Speedment 进行设置,还有如何使用 Java 8 的 Stream API 来从数据库中创建、更新、读取以及删除实体。这是你可以利用 Speedment 所能进行的操作的一个小的子集, 但已经是一个能让你上手的好的开始了。更多的示例以及更加高级的使用场景可以在GitHub-page上找到。