Java 8 Streams中的數(shù)據(jù)庫CRUD操作
接觸一個新工具的時候,剛開始要克服的***障礙就是如何讓你自己先嘗試做出一個小東西來?,F(xiàn)在你也許對 Java 8 中新的 Stream API 的運作方式在理解上比較自信,但你也許并沒用它來進行過數(shù)據(jù)庫查詢操作。為了幫助你開始使用 Stream API 來對 SQL 數(shù)據(jù)庫進行創(chuàng)建、修改和讀取操作, 我已經(jīng)在這個快速開始的教程中把它們整合到了一起。希望它能幫助你提升對流式API的使用水平!
背景
Speedment 是一個開放源代碼的工具集,它可以被用來生成 Java 實體,并且能將我們同數(shù)據(jù)庫的通信過程管理起來。你可以利用一個圖形工具連接到數(shù)據(jù)庫并生成出一套完整的 ORM 框架代碼來表示域模型。但是 Speedment 不單單只是一個代碼生成器而已,它還是一個能插入應用程序中的運行時程序,這樣就有可能將你的 Java 8 流式代碼翻譯成優(yōu)化過的SQL查詢。這也是我將會在本文中專門講述的一個部分。
生成代碼
要在一個 Maven 工程中開始使用 Speedment,需要你將下面幾行代碼添加到你的 pom.xml 文件中。在本例中,我使用的是 MySQL,而你也可以選擇使用 PostgreSQL 或者 MariaDB。面向于像Oracle這樣的專有數(shù)據(jù)庫可用于企業(yè)級客戶。
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>
現(xiàn)在你可以訪問到許多新的 Maven 資源庫,它們能讓你更加輕松的使用這個工具包。要啟動 Speedment UI, 執(zhí)行如下命令:
- mvn speedment:tool
這樣就會有一個過程引導你連接到數(shù)據(jù)庫并對代碼生成進行配置。一開始最簡單的方法就是用默認的設置先跑起來再說。當你按下生成按鈕“Generate,” Speedment 就會對你的數(shù)據(jù)庫元數(shù)據(jù)進行分析,然后在你的工程中添加像實體和實體管理器這樣的類。
初始化 Speedment
當你的域模型生成好了以后,Speedment 的設置就容易了。創(chuàng)建一個新的 Main.java 文件然后添加如下幾行代碼。你看到的類都是生成的,因此它們的命名都是根據(jù)數(shù)據(jù)庫模式、表以及列的名稱來決定的。
Main.java
- public class Main {
- public static void main(String... param) {
- final HaresApplication app = new HaresApplicationBuilder()
- .withPassword("password")
- .build();
- }
- }
上面的代碼創(chuàng)建了一個新的應用程序實體,它使用了一種生成的構造器模式。構造器是的對任何運行時配置細節(jié)的設置成為可能,例如數(shù)據(jù)庫的密碼。
當我們有了一個應用實體,就可以用它來訪問生成的實體管理器了。在這里,我的數(shù)據(jù)庫中有了四個表; “hare”, “carrot”, “human”, 以及 “friend”. (你可以在這里找到完整的數(shù)據(jù)庫定義)。
- 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);
現(xiàn)在這些實體管理器可以被用來執(zhí)行所有的CRUD操作了。
創(chuàng)建實體
創(chuàng)建實體的方式非常直接。我們就使用實體生成的實現(xiàn),把列的值設置好然后持久化到數(shù)據(jù)源就可以了。
- hares.persist(
- new HareImpl()
- .setName("Harry")
- .setColor("Gray")
- .setAge(8)
- );
persist 方法會返回一個 (潛在的) Hare 新實例,里面像“id”這種自動生成鍵已經(jīng)設置好了。如果我們想在持久化之后繼續(xù)使用 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對數(shù)據(jù)庫中的數(shù)據(jù)進行流式操作。“為什么這樣做會很酷呢?” 你可能會這樣問你自己。“如今甚至Hibernate 都已經(jīng)支持流式操作了!”這就是回答。
使用 Speedment 流式操作最美好的事情就是它們把構建流的中間和終止動作都考慮進去了。這就意味著如果你在流已經(jīng)被創(chuàng)建之后添加一個過濾器進去,那么在構建 SQL 語句時這個過濾器也會被考慮進去。
下面是一個示例,我們想要計算數(shù)據(jù)庫中 hare 記錄的總數(shù)。
- final long haresTotal = hares.stream().count();
- System.out.format("There are %d hares in total.%n", haresTotal);
這段代碼將會生成的SQL查詢?nèi)缦拢?/p>
- SELECT COUNT(id) FROM hares.hare;
這里的終止操作就是 .count() ,因此 Speedment 就知道是要創(chuàng)建一個 SELECT COUNT(…) 語句。它也知道 “hare”表的主鍵是“id”這個列,如此就有可能將發(fā)送給數(shù)據(jù)庫的整個語句 減少到這個樣子。
更加復雜的示例可能就是找出名稱以 “rry” 并且年齡大于等于 5 的兔子的數(shù)量。這個可以這樣寫:
- 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 不可以對流進行優(yōu)化的操作, 它就會像一般的 Java 8 流那被處理。我們永遠都不會限制生成的位于構建器的使用,它能是流式操作更加的高效。
- final long inefficientTotal = hares.stream()
- .filter(h -> h.getName().hashCode() == 52)
- .count();
上述代碼會產(chǎn)生一條如下極其低效的語句,但是它仍然可以跑起來。
- SELECT id,name,color,age FROM hares.hare;
更新實體
更新存在的實體和讀取以及持久化實體非常相似。在我們調用update()方法之前,對實體本地拷貝的改變,不會影響數(shù)據(jù)庫內(nèi)容。
下面,我們拿到之前使用Hare創(chuàng)建的Harry,并將他的顏色變?yōu)樽厣?/p>
- harry.setColor("brown");
- final Hare updatedHarry = hares.update(harry);
如果更新被接受了,那么管理器會返回hare的一個新的拷貝,因為我們在后面會繼續(xù)使用這個實例。就想做“創(chuàng)建”的例子中,更新可能會失敗。也許顏色被定義為“值唯一”,棕色已經(jīng)存在于hare中。那樣的話,會拋出一個SpeedmentException異常.
我們也可以通過合并多個實體到一個流中來同時更新他們。加入我們想將所有名字為Harry的hare變?yōu)樽厣?,我們可以這樣做:
- hares.stream()
- .filter(Hare.NAME.equal("Harry"))
- .map(Hare.COLOR.setTo("Brown"))
- .forEach(hares.updater()); // 更新流中存在的元素
我們還應該使用try-catch語句來確保在運行過程中有失敗發(fā)生時警告用戶。
- 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 操作就是從數(shù)據(jù)庫中刪除實體。這個操作幾乎和“更新”操作時等同的。假如說我們要把年齡超過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;
- }
總結
通過閱讀本文你已經(jīng)了解了如何在一個 Maven 工程中對 Speedment 進行設置,還有如何使用 Java 8 的 Stream API 來從數(shù)據(jù)庫中創(chuàng)建、更新、讀取以及刪除實體。這是你可以利用 Speedment 所能進行的操作的一個小的子集, 但已經(jīng)是一個能讓你上手的好的開始了。更多的示例以及更加高級的使用場景可以在GitHub-page上找到。