<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Vert.x 操作數(shù)據(jù)庫

          共 6200字,需瀏覽 13分鐘

           ·

          2021-03-26 00:32

          31d105d25d4bb70e8eeaa3086bf50b9f.webp哈嘍,大家好,歡迎閱讀閑話Java的Vert.x部分,本篇是閑話Vert.x的第四期,《Vert.x操作數(shù)據(jù)庫》。本期由閑話哥帶您了解,如何通過Vert.x來連接數(shù)據(jù)庫。

          后續(xù)所使用的代碼均開源在:https://github.com/happy-fly/wxcode


          為什么不用Mybatis或者Hibernate

          Vert.x提供異步訪問數(shù)據(jù)庫的API,可能這里有朋友會有疑惑,直接使用我們之前的熟悉的Mybatis或者Hibernate不行嗎,可行,但數(shù)據(jù)庫操作是一個耗時操作,使用傳統(tǒng)的同步模型,容易阻塞線程,導(dǎo)致整體性能下降,因此我們對于數(shù)據(jù)庫操作,需要使用Vert.x提供的異步API。

          Vert.x提供的API層級非常低,可以說是僅僅在原生JDBC基礎(chǔ)上封裝了一層異步接口。所有的對數(shù)據(jù)庫操作都需要通過編寫SQL來完成,參數(shù)的封裝和結(jié)果的獲取都需要手動的來實現(xiàn),對于習(xí)慣使用ORM框架的開發(fā)者可能會非常的不習(xí)慣。

          下面就來一個具體的例子來對數(shù)據(jù)庫進(jìn)行個crud。


          增刪改查

          Vert.x的數(shù)據(jù)庫操作還是比較簡單的,類似于Apache的DbUtils或者是Spring的JDBCTemplate?;旧暇褪菍懥薙QL,然后填上參數(shù),再讀取結(jié)果。

          我個人認(rèn)為,在Vert.x中是不提倡使用PO等對象的,數(shù)據(jù)封裝用JsonObject,那么對于數(shù)據(jù)的序列化、反序列化包括數(shù)據(jù)的傳輸,異構(gòu)平臺的數(shù)據(jù)交互就都滿足了。所以在我參與的系統(tǒng)中,都沒有涉及到類似于User,Dept之類的對象,數(shù)據(jù)就存儲到JsonObject中。

          在開發(fā)中,你需要轉(zhuǎn)變思想,不能總想著封裝對象。比如在下面的例子中,查詢數(shù)據(jù)庫的結(jié)果,官方的API就幫助我們把結(jié)果封裝到JsonObject中了,對于多行多列的結(jié)果,就會封裝為List<JsonObject>。

          廢話不多說,我們實現(xiàn)一個基于User信息的增刪改查。

          準(zhǔn)備工作

          引入依賴包,這里需要引入vertx-jdbc和mysql驅(qū)動包

          <dependency>    <groupId>io.vertx</groupId>    <artifactId>vertx-jdbc-client</artifactId>    <version>${vertx.version}</version></dependency><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>8.0.13</version></dependency>

          主方法

          創(chuàng)建一個Verticle,在Verticle的start方法中寫我們的測試代碼,最終如下。

          ab5ea12b9025c12da2e1f4df0c4f19db.webp

          運行結(jié)果會按新增、修改、查詢、刪除的順序執(zhí)行嗎?

          不會的!這四個方法會同時執(zhí)行,誰先搶到CPU的時間片,誰就先執(zhí)行。因為這四個方法都是異步的。

          新增(基礎(chǔ))

          update方法接收兩個參數(shù),第一個參數(shù)是我們要執(zhí)行的SQL。第二個參數(shù)是HandlerHandler<AsyncResult<UpdateResult>> handler。這種參數(shù)我們不是第一次遇見了,后續(xù)我會帶大家分析下Handler的實現(xiàn)機(jī)制。Handler中有一個泛型參數(shù)AsyncResult,標(biāo)識的是異步結(jié)果,也有一個泛型參數(shù)UpdateResult,這個參數(shù)就是異步方法執(zhí)行完畢后響應(yīng)的數(shù)據(jù)。

          update方法進(jìn)行了重載,可以在SQL和Handler中間傳入一個JsonArray。對于SQL中帶有參數(shù)的情景,在SQL中用“?”進(jìn)行替代,然后將參數(shù)的值放到JsonArray中,傳入到update方法中即可。參考后續(xù)修改操作的代碼!

          private void save(JDBCClient client) {    String sql = "insert into user(name) values ('zhangsan')";    client.update(sql, r -> {        if (r.succeeded()) {            System.out.println("保存成功");        }    });}

          修改(SQL中帶參數(shù))

          代碼結(jié)構(gòu)和上面的新增是完全相同的,這里演示了SQL中帶參數(shù)是情景,這種情景在開發(fā)中會用的更多。

          private void update(JDBCClient client) {    String sql = "update user set name = ? where id = ?";    // 帶參數(shù)    JsonArray params = new JsonArray()            .add("lisi")            .add(1);    client.updateWithParams(sql, params, r -> {        if (r.succeeded()) {            System.out.println("刪除成功");        }    });}

          刪除

          這里只是換了SQL,其他的都不變。

          private void delete(JDBCClient client) {    String sql = "delete from user";    client.update(sql, r -> {        if (r.succeeded()) {            System.out.println("刪除成功");        }    });}

          查詢

          查詢主要關(guān)注點是結(jié)果的封裝。

          1. 多行多列 :queryWithParams List<JsonObject>

          2. 單行多列:querySingleWithParams JsonArray

          3. 多行單列:同多行多列

          4. 單行單列:同單行多列

          我習(xí)慣性都用queryWithParams,然后對結(jié)果進(jìn)行處理后拿到想要的數(shù)據(jù),下面是一個單行多列的例子。

          private void query(JDBCClient client) {    String sql = "select * from sys_user where id = ?";    JsonArray params = new JsonArray().add(1);    client.queryWithParams(sql, params, r -> {        if (r.succeeded()) {            List<JsonObject> rows = r.result().getRows();            System.out.println(rows.get(0));        } else {            System.out.println(r.cause());        }    });}


          CRUD小結(jié)

          通過上面的代碼,可以很輕松的看到,基本的增刪改查還是比較簡單的。貼近于原生的寫法。

          對于剛開始接觸Vert.x的朋友,一定感覺非常不適應(yīng),首先是這么原始的嗎,我們之前都jpa了,這是哪個年代的框架。第二個就是“對象”呢?為啥用JsonObject啊,我怎么知道JsonObject里面到底有哪些屬性呢?

          對于這兩個問題我現(xiàn)在沒法給到具體的答案,畢竟用過DbUtils的朋友可能也不多,靈活和性能也未必體會的到。對于數(shù)據(jù)封裝到JsonObject中,這個真是利弊都有,禍兮福所倚,福兮禍所伏。福禍相依,有無相生,難易相成,長短相形,高下相傾,音聲相和,前后相隨。

          到這里數(shù)據(jù)庫操作就結(jié)束了嗎?沒有,還有兩個很難解決的問題。

          • 循環(huán)執(zhí)行多條SQL該怎么處理呢?

          • 如何執(zhí)行事務(wù)操作?

          這兩個問題都不好解決,而且寫起來都非常的惡心,我們挨個的看下吧。


          循環(huán)執(zhí)行SQL

          循環(huán)執(zhí)行有幾種表現(xiàn)形式

          • 循環(huán)相同的SQL,參數(shù)不同,且參數(shù)提前可以確定

          • 循環(huán)相同的SQL,次數(shù)在可控范圍之內(nèi)

          • 循環(huán)相同的SQL,參數(shù)不同,參數(shù)依賴上一個SQL的結(jié)果

          對于前兩種形式,都很好處理,第一種可以使用updateBatch提供的批量操作。第二種既然是次數(shù)可控,那就用Vert.x提供的fluent編程風(fēng)格,一個接一個的處理,雖然low,但也能解決問題。最麻煩的是第三種形式。

          批量執(zhí)行相同SQL

          批量執(zhí)行相同SQL,可以通過batchWithParams方法,這個方法中就接收了執(zhí)行的SQL和SQL所依賴的參數(shù)。這個參數(shù)的形式是List<JsonArray>。那么這樣我們就能很輕松的解決批量的問題。

          按照面向?qū)ο蟮乃季S方式,我們期望直接通過client.batchWithParams,當(dāng)你這么寫的時候,你會發(fā)現(xiàn)找不到這個方法。其實,這個方法是在SQLConnection對象中的,因此,我們要先來獲得連接對象。

          7688e25c5d8ffd729f1edb2b21ff165a.webp

          上面的例子中,會執(zhí)行兩次SQL,參數(shù)是List集合里的兩個JsonArray元素。

          批量執(zhí)行的SQL,執(zhí)行次數(shù)在可控范圍

          Vert.x提供了Fluent編程風(fēng)格的API,就是說update方法后還可以繼續(xù)update方法,直到最后一個操作執(zhí)行完畢。如果有5個操作,那們就是5個update就可以了。這種方式雖然顯得low,但也能解決問題。

          94f035a60fa18c2c6e57c18cd46b584e.webp

          這種操作方式無法實現(xiàn)下一個操的條件是上一個操作的結(jié)果。所有的操作必須是沒有依賴的,誰先執(zhí)行都可以。

          批量的SQL,下一個SQL依賴上一個SQL的結(jié)果

          這是異步帶來的問題,就是循環(huán),且依賴的問題。因為異步響應(yīng)結(jié)果是在回調(diào)里面的,不是同步的響應(yīng),因此簡單的批量,你并不能通過一個for來解決。按照思路,就是在結(jié)果中再去封裝結(jié)果,因為你不知道有多少層嵌套,所有這樣是顯然不可行的,那改如何解決呢?

          可以通過rxjava,這里簡單提供一種實現(xiàn)的思路,非常復(fù)雜。

          1、引入rxjava的依賴

          <dependency>    <groupId>io.vertx</groupId>    <artifactId>vertx-rx-java</artifactId>    <version>${vertx.version}</version></dependency>

          2、核心執(zhí)行程序

          7317269f6cd86de5845fcfbf240c59e2.webp

          3、rxExecuter方法實現(xiàn)

          adf8dee0048bc87c7145e4a26a06acaf.webp

          4、execute方法實現(xiàn)

          93554de42eb6ad99b5c05dd4f5027312.webp

          到這里,最為強(qiáng)大的循環(huán)就完事了,是不是比你想象的要復(fù)雜很多。沒辦法,想要循環(huán)異步的操作就要這么實現(xiàn)。


          事務(wù)

          數(shù)據(jù)庫操作,事務(wù)是不可避免的,先來回顧下jdbc如何開啟事務(wù)?

          1. 獲取連接

          2. 設(shè)置不自動提交事務(wù)(setAutoCommit?= false)

          3. 執(zhí)行SQL操作

          4. 提交或者回滾事務(wù)

          5. 關(guān)閉資源

          那么我們分析下上面的這幾個步驟,在同步的編程模型下,每個步驟都很簡單,幾乎一行代碼就搞定了??墒堑搅水惒侥P拖?,我們首先應(yīng)該考慮的就是這個操作是不是一個耗時操作(這里簡單認(rèn)為通過網(wǎng)絡(luò)連接的,就是耗時操作),如果是耗時操作,我們就需要對其進(jìn)行異步的封裝。

          那么上面的5個步驟中,都需要和數(shù)據(jù)庫通信,也就需要走網(wǎng)絡(luò),網(wǎng)絡(luò)相比較CPU的處理速度那真是太慢了,因此肯定都是耗時操作。那也就意味著,這些都是異步的。

          /** * 事務(wù) * <p> * 獲取連接 * 設(shè)置不自動提交事務(wù)(setAutoCommit = false) * 執(zhí)行SQL操作 * 提交或者回滾事務(wù) * 關(guān)閉資源 * * @param client */private void tx(JDBCClient client) {  client.getConnection(c->{    if(c.succeeded()) {      SQLConnection connection = c.result();      // 關(guān)閉事務(wù)自動提交      connection.setAutoCommit(false, a->{        if(a.succeeded()) {          // 執(zhí)行SQL          connection.update("sql1", r1->{            if(r1.succeeded()) {              connection.update("sql2",r2->{                if(r2.succeeded()) {                  // 提交事務(wù)                  connection.commit(o->{});                } else {                  // 操作2執(zhí)行失敗,事務(wù)回滾                  connection.rollback(r->{});                }              });            }else {              // 操作1執(zhí)行失敗              connection.rollback(b->{                if(b.succeeded()) {                  // 回滾成功                }              });            }          });        } else {          // 開始事務(wù)失敗        }      });    } else {      // 獲取連接失敗    }  });}

          沒有辦法,這里就是這樣,開啟事務(wù)也就只能這么實現(xiàn),著實非常麻煩。這里同樣可以使用rx來降低開發(fā)難度。


          總結(jié)

          到這里,相信你對數(shù)據(jù)庫的操作就已經(jīng)很熟悉了。習(xí)慣了Mybatis或者JPA的你,是不是一時還不能接受。你是不是還在想著我該先創(chuàng)建個對象,然后將結(jié)果封裝到對象里。

          上面的想法是完全可以實現(xiàn)的,但是,用了很久的jdbc之后,我會感覺到,純的jdbc未必不是個好的寫法。我在一些公司的小的項目架構(gòu)中,也會采用Spring+DbUtils的形式,在簡單、靈活以及性能的方面也是提高了不少。

          所以拋開技術(shù)實現(xiàn)不談,還是要貼合業(yè)務(wù)需求。存在的即合理的。

          The End

          下期預(yù)告

          我們系統(tǒng)中操作比較頻繁的除了數(shù)據(jù)庫,就屬于Redis了,幾乎所有的項目都離不開高性能的緩存服務(wù)器Redis。下一篇我來帶大家看看Vert.x是如何和Redis進(jìn)行交互的。


          瀏覽 98
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  啊啊啊啊被操逼了好爽视频免费 | 亚洲高清视频在线看! | 青青草国产亚洲精品久久 | 五十路AV熟女片 | 中文字幕AV电影 |