java导出数据透视表_使用数据库中的Java流制作数据透视表
java導(dǎo)出數(shù)據(jù)透視表
來自數(shù)據(jù)庫行和表的原始數(shù)據(jù)不能為人類讀者提供太多了解。 相反,如果我們對數(shù)據(jù)執(zhí)行某種聚合,則人類更有可能看到數(shù)據(jù)模式
在向我們展示之前。 數(shù)據(jù)透視表是聚合的一種特定形式,我們可以在其中應(yīng)用排序,求平均值或求和之類的操作,也可以對列值進(jìn)行分組。
在本文中,我將展示如何在不編寫SQL的情況下就可以從純Java數(shù)據(jù)庫中計算數(shù)據(jù)的數(shù)據(jù)透視表。 您可以輕松地重用和修改本文中的示例,以滿足您自己的特定需求。
在以下示例中,我使用了開源Speedment (它是Java Stream ORM)和MySQL的開源Sakila電影數(shù)據(jù)庫內(nèi)容。 Speedment適用于任何主要的關(guān)系數(shù)據(jù)庫類型,例如MySQL,PostgreSQL,Oracle,MariaDB,Microsoft SQL Server,DB2,AS400等。
旋轉(zhuǎn)
我將構(gòu)造一個Map的Actor對象,并為每個Actor ,相應(yīng)的List電影,一個特殊的電影分級的Actor出現(xiàn)在這里是為特定的樞軸如何進(jìn)入一個例子。 Actor可能看起來像口頭上表示:
“約翰·多伊(John Doe)參加了9部評級為'PG-13'的電影和4部評級為'R'的電影”。
我們將計算數(shù)據(jù)庫中所有參與者的樞軸值。 Sakila數(shù)據(jù)庫具有此特定應(yīng)用程序感興趣的三個表:
1)“電影”包含所有電影以及如何評價電影(例如“ PG-13”,“ R”等)。
2)包含(組成)演員的“演員”(例如“ MICHAEL BOLGER”,“ LAURA BRODY”等)。
3)“電影演員”,以多對多的關(guān)系將電影和演員聯(lián)系在一起。
解決方案的第一部分涉及將這三個表連接在一起。 聯(lián)接是使用Speedment的JoinComponent創(chuàng)建的,可以通過以下方式獲得:
// Visit https://github.com/speedment/speedment // to see how a Speedment app is created. It is easy! Speedment app = …;JoinComponent joinComponent = app.getOrThrow(JoinComponent.class);一旦有了JoinComponent ,就可以開始定義計算關(guān)系表所需的Join關(guān)系:
Join<Tuple3<FilmActor, Film, Actor>> join = joinComponent.from(FilmActorManager.IDENTIFIER).innerJoinOn(Film.FILM_ID).equal(FilmActor.FILM_ID).innerJoinOn(Actor.ACTOR_ID).equal(FilmActor.ACTOR_ID).build(Tuples::of);build()采用方法引用Tuples::of ,該方法引用將解析為采用三個類型的實體的構(gòu)造函數(shù)。 FilmActor , Film和Actor ,這將創(chuàng)建一個包含這些特定實體的復(fù)合不可變Tuple3 。 元組內(nèi)置于Speedment中。
有了Join對象,我們現(xiàn)在可以使用從Joi??n對象獲得的標(biāo)準(zhǔn)Java Stream創(chuàng)建數(shù)據(jù)透視圖:
Map<Actor, Map<String, Long>> pivot = join.stream().collect(groupingBy(// Applies Actor as a first classifierTuple3::get2,groupingBy(// Applies rating as second level classifiertu -> tu.get1().getRating().get(),counting() // Counts the elements )));現(xiàn)在已經(jīng)計算了樞軸Map ,我們可以像這樣打印其內(nèi)容:
// pivot keys: Actor, values: Map<String, Long> pivot.forEach((k, v) -> { System.out.format("%22s %5s %n",k.getFirstName() + " " + k.getLastName(),V); });這將產(chǎn)生以下輸出:
MICHAEL BOLGER {PG-13=9, R=3, NC-17=6, PG=4, G=8} LAURA BRODY {PG-13=8, R=3, NC-17=6, PG=6, G=3} CAMERON ZELLWEGER {PG-13=8, R=2, NC-17=3, PG=15, G=5} ...任務(wù)完成! 在上面的代碼中,方法Tuple3::get2將從元組( Actor )中檢索第三個元素,而方法tu.get1()將從元組( Film )中檢索第二個元素。
Speedment將自動從Java渲染SQL代碼,并將結(jié)果轉(zhuǎn)換為Java Stream。 如果啟用流日志記錄,我們可以確切看到如何呈現(xiàn)SQL:
SELECT A.`actor_id`,A.`film_id`,A.`last_update`, B.`film_id`,B.`title`,B.`description`,B.`release_year`,B.`language_id`,B.`original_language_id`,B.`rental_duration`,B.`rental_rate`,B.`length`,B.`replacement_cost`,B.`rating`,B.`special_features`,B.`last_update`, C.`actor_id`,C.`first_name`,C.`last_name`,C.`last_update` FROM `sakila`.`film_actor` AS A INNER JOIN `sakila`.`film` AS B ON (B.`film_id` = A.`film_id`) INNER JOIN `sakila`.`actor` AS C ON (C.`actor_id` = A.`actor_id`)加入自定義元組
正如我們在上面的示例中所注意到的,由于在連接階段僅將FilmActor對象用于將Film和Actor實體鏈接在一起,因此我們在Stream中沒有實際使用FilmActor對象。 此外,通用Tuple3有一般get0() get1()和get2()是沒有說他們裝的是什么東西的方法。
所有這些都可以通過定義我們自己的稱為ActorRating的自定義“元組”來ActorRating如下所示:
private static class ActorRating {private final Actor actor;private final String rating;public ActorRating(FilmActor fa, Film film, Actor actor) {// fa is not used. See below whythis.actor = actor;this.rating = film.getRating().get();}public Actor actor() {return actor;}public String rating() {return rating;}}當(dāng)使用build()方法build() Join對象時,我們可以提供一個自定義構(gòu)造函數(shù),該構(gòu)造函數(shù)要應(yīng)用于數(shù)據(jù)庫的傳入實體。 這是我們將要使用的功能,如下所示:
Join<ActorRating> join = joinComponent.from(FilmActorManager.IDENTIFIER).innerJoinOn(Film.FILM_ID).equal(FilmActor.FILM_ID).innerJoinOn(Actor.ACTOR_ID).equal(FilmActor.ACTOR_ID).build(ActorRating::new); // Use a custom constructorMap<Actor, Map<String, Long>> pivot = join.stream().collect(groupingBy(ActorRating::actor,groupingBy(ActorRating::rating,counting())));在此示例中,我們證明了帶有構(gòu)造函數(shù)的類(方法參考ActorRating:new被解析為new ActorRating(fa, actor, film) ),該FilmActor函數(shù)只是完全丟棄了鏈接的FilmActor對象。 該類還為其屬性提供了更好的名稱,這使代碼更具可讀性。 帶有自定義ActorRating類的解決方案將產(chǎn)生與第一個示例完全相同的輸出結(jié)果,但使用時看起來要好得多。 我認(rèn)為在大多數(shù)情況下,與使用通用元組相比,編寫自定義元組值得付出額外的精力。
使用平行旋轉(zhuǎn)
Speedment的一件很酷的事情是,它支持開箱即用的Stream方法parallel() 。 因此,如果您的服務(wù)器具有許多CPU,則在運行數(shù)據(jù)庫查詢和聯(lián)接時可以利用所有這些CPU內(nèi)核。 這就是并行樞軸的樣子:
Map<Actor, Map<String, Long>> pivot = join.stream().parallel() // Make our Stream parallel.collect(groupingBy(ActorRating::actor,groupingBy(ActorRating::rating,counting())));我們只需要添加一行代碼即可進(jìn)行并行聚合。 當(dāng)我們達(dá)到1024個元素時,將啟動默認(rèn)的并行拆分策略。 因此,并行樞轉(zhuǎn)將僅在大于此值的表或聯(lián)接上進(jìn)行。 應(yīng)該注意的是,Sakila數(shù)據(jù)庫僅包含1000部影片,因此我們必須在更大的數(shù)據(jù)庫上運行代碼才能真正受益于并行性。
試試看!
在本文中,我們展示了如何在不編寫任何SQL代碼的情況下,就可以使用Java從數(shù)據(jù)庫計算數(shù)據(jù)透視表。 訪問GitHub上的 Speedment開源以了解更多信息。
在《用戶指南》中閱讀有關(guān)其他功能的更多信息。
翻譯自: https://www.javacodegeeks.com/2018/05/making-pivot-tables-with-java-streams-from-databases.html
java導(dǎo)出數(shù)據(jù)透視表
總結(jié)
以上是生活随笔為你收集整理的java导出数据透视表_使用数据库中的Java流制作数据透视表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux架设服务器相关书籍(linux
- 下一篇: 怎么才从光盘启动电脑上(用光盘怎么启动电