一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Vert-x-通過異步的方式使用JDBC連接SQL

Vert-x-通過異步的方式使用JDBC連接SQL

2020-03-22 13:18不安分的碼農 JAVA教程

在這篇文章中,我們將會看到怎樣在vert.x應用中使用HSQL,當然也可以使用任意JDBC,以及使用vertx-jdbc-client提供的異步的API,這篇文章的代碼在github

在這篇文章中,我們將會看到怎樣在vert.x應用中使用HSQL,當然也可以使用任意JDBC,以及使用vertx-jdbc-client提供的異步的API,這篇文章的代碼在github。

異步?

vert.x一個很重要的特點就是它的異步性。使用異步的API,不需要等結果返回,當有結果返回時,vert.x會主動通知。為了說明這個,我們來看一個簡單的例子。

我們假設有個add方法。一般來說,會像int r = add(1, 1)這樣來使用它。這是一個同步的API,所以你必須等到返回結果。異步的API會是這樣:add(1, 1, r -> { /*do something with the result*/})。在這個版本中,你傳入了一個Handler,當結果計算出來時才被調用。這個方法不返回任何東西,實現如下:

?
1
2
3
4
public void add(int a, int b, Handler<Integer> resultHandler) {
int r = a + b;
resultHandler.handle(r);
}

為了避免混淆概念,異步API并不是多線程。像我們在add例子里看到的,并沒有涉及多線程。

異步JDBC

看了一些基本的異步的API,現在了解下vertx-jdbc-client。這個組件能夠讓我們通過JDBC driver與數據庫交互。這些交互都是異步的,以前這樣:

?
1
2
String sql = "SELECT * FROM Products";
ResultSet rs = stmt.executeQuery(sql);

現在要這樣:

?
1
2
3
connection.query("SELECT * FROM Products", result -> {
// do something with the result
});

這個模型更高效,當結果出來后vert.x通知,避免了等待結果。

增加maven依賴

在pom.xml文件中增加兩個 Maven dependencies

?
1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-jdbc-client</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.3</version>
</dependency>

第一個依賴提供了vertx-jdbc-client,第二個提供了HSQL JDBC的驅動。如果你想使用另外一個數據庫,修改這個依賴,同時你還需要修改JDBC url和JDBC driver名。

初始化JDBC client

創建JDBC 客戶端(client):

在MyFirstVerticle類中,聲明一個新變量JDBCClient jdbc,并且在start方法中添加:

jdbc = JDBCClient.createShared(vertx, config(), "My-Whisky-Collection");

創建了一個JDBC client實例,使用verticle的配置文件配置JDBC client。這個配置文件需要提供下面的配置才能讓JDBC client正常工作:

url-JDBC url,例如:jdbc:hsqldb:mem:db?shutdown=true
_driver class-JDBC的驅動,例如:org.hsqldb.jdbcDriver

有了client,接下來需要連接數據庫。連接數據庫是通過使用jdbc.getConnection來實現的,jdbc.getConnection需要傳入一個Handler<AsyncResult<SQLConnection>>參數。我們深入的了解下這個類型。首先,這是一個Handler,因此當結果準備好時它就會被調用。這個結果是AsyncResult<SQLConnection>的一個實例。AsyncResult是vert.x提供的一個結構,使用它能夠知道連接數據庫的操作是成功或失敗了。如果成功了,它就會提供一個結果,這里結果是一個SQLConnection的實例。

當你接收一個AsyncResult的實例時,代碼通常是:

?
1
2
3
4
5
6
7
if (ar.failed()) {
System.err.println("The operation has failed...: "
+ ar.cause().getMessage());
} else {
// Use the result:
result = ar.result();
}

需要獲取到SQLConnection,然后啟動rest的應用。因為變成了異步的,這需要改變啟動應用的方式。因此,如果將啟動序列劃分成多塊:

?
1
2
3
4
5
6
startBackend(
(connection) -> createSomeData(connection,
(nothing) -> startWebApp(
(http) -> completeStartup(http, fut)
), fut
), fut);

startBackend- 獲取SQLConnection對象,然后調用下一步
createSomeData- 初始化數據庫并插入數據。當完成后,調用下一步
startWebApp- 啟動web應用
completeStartup- 最后完成啟動
fut由vert.x傳入,通知已經啟動或者啟動過程中遇到的問題。

startBackend方法:

?
1
2
3
4
5
6
7
8
9
private void startBackend(Handler<AsyncResult<SQLConnection>> next, Future<Void> fut) {
jdbc.getConnection(ar -> {
if (ar.failed()) {
fut.fail(ar.cause());
} else {
next.handle(Future.succeededFuture(ar.result()));
}
});
}

這個方法獲取了一個SQLConnection對象,檢查操作是否完成。如果成功,會調用下一步。失敗了,就會報告一個錯誤。其他的方法遵循同樣的模式:

檢查上一步操作是否成功

處理業務邏輯

調用下一步

SQL

客戶端已經準備好了,現在寫SQL。從createSomeData方法開始,這個方法也是啟動順序中的一部分:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void createSomeData(AsyncResult<SQLConnection> result,
Handler<AsyncResult<Void>> next, Future<Void> fut) {
if (result.failed()) {
fut.fail(result.cause());
} else {
SQLConnection connection = result.result();
connection.execute(
"CREATE TABLE IF NOT EXISTS Whisky (id INTEGER IDENTITY, name varchar(100), " +
"origin varchar(100))",
ar -> {
if (ar.failed()) {
fut.fail(ar.cause());
connection.close();
return;
}
connection.query("SELECT * FROM Whisky", select -> {
if (select.failed()) {
fut.fail(ar.cause());
connection.close();
return;
}
if (select.result().getNumRows() == 0) {
insert(
new Whisky("Bowmore 15 Years Laimrig", "Scotland, Islay"),
connection,
(v) -> insert(new Whisky("Talisker 57° North", "Scotland, Island"),
connection,
(r) -> {
next.handle(Future.<Void>succeededFuture());
connection.close();
}));
} else {
next.handle(Future.<Void>succeededFuture());
connection.close();
}
});
});
}
}

這個方法檢查SQLConnection是否可用,然后執行一些SQL語句。首先,如果表不存在就創建表。看看下面代碼:

?
1
2
3
4
connection.execute(
SQL statement,
handler called when the statement has been executed
)

handler接收AsyncResult<Void>,例如:只有是通知而已,沒有實際返回的結果。

關閉連接

操作完成后,別忘了關閉SQL鏈接。這個連接會被放入連接池并且可以被重復利用。

在這個handler的代碼里,檢查了statement是否正確的執行了,如果正確,我們接下來檢查表是否含有數據,如果沒有,將會使用insert方法插入數據:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void insert(Whisky whisky, SQLConnection connection, Handler<AsyncResult<Whisky>> next) {
String sql = "INSERT INTO Whisky (name, origin) VALUES ?, ?";
connection.updateWithParams(sql,
new JsonArray().add(whisky.getName()).add(whisky.getOrigin()),
(ar) -> {
if (ar.failed()) {
next.handle(Future.failedFuture(ar.cause()));
return;
}
UpdateResult result = ar.result();
// Build a new whisky instance with the generated id.
Whisky w = new Whisky(result.getKeys().getInteger(0), whisky.getName(), whisky.getOrigin());
next.handle(Future.succeededFuture(w));
});
}

這個方法使用帶有INSERT(插入)statement(聲明)的upateWithParams方法,且傳入了值。這個方法避免了SQL注入。一旦statement執行了(當數據庫沒有此條數據就會創建),就創建一個新的Whisky對象,自動生成ID。

帶有數據庫(SQL)的REST

上面的方法都是啟動順序的一部分。但是,關于調用REST API的方法又是怎么樣的呢?以getAll方法為例。這個方法被web應用前端調用,并檢索存儲的所有的產品:

?
1
2
3
4
5
6
7
8
9
10
11
12
private void getAll(RoutingContext routingContext) {
jdbc.getConnection(ar -> {
SQLConnection connection = ar.result();
connection.query("SELECT * FROM Whisky", result -> {
List<Whisky> whiskies = result.result().getRows().stream().map(Whisky::new).collect(Collectors.toList());
routingContext.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(whiskies));
connection.close(); // Close the connection
});
});
}

這個方法獲得了一個SQLConnection對象,然后發出一個查詢。一旦獲取到查詢結果,它會像之前的方法一樣寫HTTP response。getOne、deleteOne、updateOne和addOne方法都是一樣的。注意,在response之后,需要要關閉SQL連接。

看下傳入到query方法的handler提供的結果。獲取了一個包含了查詢結果的ResultSet。每一行都是一個JsonObject,因此,如果你有一個數據對象使用JsonObject作為唯一的參數,那么創建這個對象很簡單。

測試

需要小小的更新下測試程序,增加配置JDBCClient。在MyFirstVerticleTest類中,將setUp方法中創建的DeploymentOption對象修改成:

?
1
2
3
4
5
6
DeploymentOptions options = new DeploymentOptions()
.setConfig(new JsonObject()
.put("http.port", port)
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver")
);

除了http.port,還配置了JDBC url和JDBC驅動。測試時,使用的是一個內存數據庫。在src/test/resources/my-it-config.json文件中也要做同樣的修改。

?
1
2
3
4
5
{
"http.port": ${http.port},
"url": "jdbc:hsqldb:mem:it-test?shutdown=true",
"driver_class": "org.hsqldb.jdbcDriver"
}

src/main/conf/my-application-conf.json文件也同樣需要修改,這不是為了測試,而是為了運行這個應用:

?
1
2
3
4
5
{
"http.port" : 8082,
"url": "jdbc:hsqldb:file:db/whiskies",
"driver_class": "org.hsqldb.jdbcDriver"
}

這里這個JDBC url和上一個文件的有點不一樣,因為需要將數據庫存儲到硬盤中。

展示時間!

開始構建程序:

mvn clean package

沒有修改API(沒有更改發布的java文件和REST接口),測試應該是可以順利的運行的。

啟動應用:

java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar -conf src/main/conf/my-application-conf.json

訪問http://localhost:8082/assets/index.html,然后,你可以看到這個應用使用的是數據庫了。這一次,就算重啟應用,這些數據仍然在,因為存儲產品被持久化到硬盤里了。

總結

這篇文章中,知道了怎么在vert.x里使用JDBC數據庫,并沒有很多復雜的東西。開始可能會被這個異步的開發模型驚訝到,但是,一旦你開始使用了,你就很難再回去了。

下一次,我們將看到這個應用怎么使用mongoDB來替換HSQL。

歡迎關注<a href="http://quanke.name/"  rel="nofollow" ></a>

交流群:231419585

轉載請注明出處,謝謝

延伸 · 閱讀

精彩推薦
  • JAVA教程java編程的30個建議

    java編程的30個建議

    這篇文章主要介紹了java編程的30個建議,需要的朋友可以參考下 ...

    Java教程網3022019-11-23
  • JAVA教程java計算時間差的方法

    java計算時間差的方法

    這篇文章主要介紹了java計算時間差的方法,涉及java針對時間的轉換與計算相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下 ...

    答復哈2512019-12-27
  • JAVA教程Java中final變量使用總結

    Java中final變量使用總結

    這篇文章主要介紹了Java中final變量使用總結,final關鍵字可用于變量聲明,一旦該變量被設定,就不可以再改變該變量的值,通常final定義的變量為常量,需要的朋...

    junjie4962019-12-21
  • JAVA教程java編程實現獲取服務器IP地址及MAC地址的方法

    java編程實現獲取服務器IP地址及MAC地址的方法

    這篇文章主要介紹了java編程實現獲取機器IP地址及MAC地址的方法,實例分析了Java分別針對單網卡及多網卡的情況下獲取服務器IP地址與MAC地址的相關技巧,需...

    jdkleo1352020-01-21
  • JAVA教程詳解Java線程編程中的volatile關鍵字的作用

    詳解Java線程編程中的volatile關鍵字的作用

    這篇文章主要介紹了Java線程編程中的volatile關鍵字的作用,針對其禁止進行指令重排序和讀寫內存方面著重講解,需要的朋友可以參考下 ...

    程曉明1472020-03-09
  • JAVA教程java遍歷讀取整個redis數據庫實例

    java遍歷讀取整個redis數據庫實例

    這篇文章主要介紹了java遍歷讀取整個redis數據庫實例,使用支持正則表達式的key搜索方法jedis.keys(“*”)實現,需要的朋友可以參考下 ...

    Java教程網1612019-11-23
  • JAVA教程JAVA時間日期處理類實例

    JAVA時間日期處理類實例

    這篇文章主要介紹了JAVA時間日期處理類,可實現遍歷兩個日期之間的每一天的功能,涉及針對日期的常見操作技巧,需要的朋友可以參考下 ...

    MagicYK2982019-12-16
  • JAVA教程淺析Java方法傳值和傳引用問題

    淺析Java方法傳值和傳引用問題

    這篇文章主要是對Java方法傳值和傳引用問題進行了詳細的介紹,需要的朋友可以過來參考下,希望對大家有所幫助 ...

    java之家4072019-10-24
主站蜘蛛池模板: 天天色天天综合 | 四虎在线精品免费高清在线 | fc2免费人成为视频 eeuss18影院www国产 | 边摸边吃奶又黄激烈视频韩国 | 韩国三级在线高速影院 | www.九九| 暖暖 免费 高清 日本 在线 | 成版人快猫永久破解版 | 国产小青蛙 | 公交车揉捏大乳呻吟喘娇 | 午夜在线观看免费观看 视频 | 午夜办公室在线观看高清电影 | 久久国产乱子伦免费精品 | 波多野结衣一区 | 91高清国产视频 | 亚洲精品久久久久AV无码 | 美女张开腿黄网站免费精品动漫 | eeuss免费快捷 | 亚偷熟乱区视频在线观看 | 国产精品久久久久久搜索 | 日本亚欧乱色视频在线观看 | 91热爆在线 | 日本私人影院 | 国产草草视频 | 9热在线精品视频观看 | 秀逼逼| 欧美一卡二卡科技有限公司 | 日韩性公交车上xxhd免费 | 天堂资源wwww在线看 | 免费视频一区二区 | 亚洲444777KKK在线观看 | 美女的隐私无遮挡的网页 | 亚洲男人天堂久久 | 国产欧美日韩精品一区二 | 日本精品中文字幕在线播放 | 日韩在线资源 | 男女车车好快的车车免费网站 | sihu国产午夜精品一区二区三区 | 色综合精品 | 调教扩张宫颈女人惨叫 | 极品ts赵恩静和直男激战啪啪 |