Vert.x 的核心優勢在於非阻塞異步模型。傳統 JDBC 同步阻塞操作會佔用事件循環線程,嚴重影響吞吐量。vertx-jdbc-client 提供異步 API,讓資料庫操作不阻塞事件循環,充分發揮 Vert.x 高併發優勢。
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-jdbc-client</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.7.1</version>
</dependency>
Vert.x 使用 Handler<AsyncResult<T>> 模式處理異步結果:
// 同步風格 (阻塞)
ResultSet rs = stmt.executeQuery("SELECT * FROM Products");
// 異步風格 (非阻塞)
connection.query("SELECT * FROM Products", result -> {
if (result.succeeded()) {
List rows = result.result().getRows();
// 處理結果...
} else {
result.cause().printStackTrace();
}
});
public class MainVerticle extends AbstractVerticle {
private JDBCClient jdbcClient;
@Override
public void start(Promise startPromise) {
// 從配置讀取資料庫參數
JsonObject config = config();
// 建立共享連接池
jdbcClient = JDBCClient.createShared(vertx, config, "My-Whisky-Collection");
// 啟動流程:連接 → 初始化資料 → 啟動 Web 服務
startBackend(conn ->
createSomeData(conn, nothing ->
startWebApp(http ->
completeStartup(http, startPromise), startPromise), startPromise),
startPromise);
}
private void startBackend(Handler> next, Promise fut) {
jdbcClient.getConnection(ar -> {
if (ar.failed()) {
fut.fail(ar.cause());
} else {
next.handle(Future.succeededFuture(ar.result()));
}
});
}
}
{
"url": "jdbc:hsqldb:mem:db?shutdown=true",
"driver_class": "org.hsqldb.jdbcDriver",
"max_pool_size": 30,
"user": "SA",
"password": ""
}
private void createSomeData(AsyncResultresult, Handler > next, Promise fut) { if (result.failed()) { fut.fail(result.cause()); return; } SQLConnection conn = result.result(); // 建表 conn.execute("CREATE TABLE IF NOT EXISTS Whisky (id INTEGER IDENTITY, name VARCHAR(100), origin VARCHAR(100))", ar -> { if (ar.failed()) { fut.fail(ar.cause()); closeQuietly(conn); return; } // 檢查是否有資料 conn.query("SELECT * FROM Whisky", select -> { if (select.failed()) { fut.fail(select.cause()); closeQuietly(conn); return; } if (select.result().getNumRows() == 0) { // 插入初始資料 insert(new Whisky("Bowmore 15 Years Laimrig", "Scotland, Islay"), conn, v -> insert(new Whisky("Talisker 57° North", "Scotland, Island"), conn, r -> { next.handle(Future.succeededFuture()); closeQuietly(conn); })); } else { next.handle(Future.succeededFuture()); closeQuietly(conn); } }); }); } private void insert(Whisky whisky, SQLConnection conn, Handler > next) { String sql = "INSERT INTO Whisky (name, origin) VALUES (?, ?)"; conn.updateWithParams(sql, new JsonArray().add(whisky.getName()).add(whisky.getOrigin()), ar -> { if (ar.failed()) { next.handle(Future.failedFuture(ar.cause())); return; } // 取得自動生成的 ID int id = ar.result().getKeys().getInteger(0); next.handle(Future.succeededFuture(new Whisky(id, whisky.getName(), whisky.getOrigin()))); }); }
private void getAll(RoutingContext rc) {
jdbcClient.getConnection(ar -> {
if (ar.failed()) {
rc.fail(ar.cause());
return;
}
SQLConnection conn = ar.result();
conn.query("SELECT * FROM Whisky", result -> {
if (result.failed()) {
rc.fail(result.cause());
} else {
List whiskies = result.result().getRows().stream()
.map(Whisky::new)
.collect(Collectors.toList());
rc.response()
.putHeader("Content-Type", "application/json; charset=utf-8")
.end(Json.encodePrettily(whiskies));
}
closeQuietly(conn); // 必須釋放連接回池
});
});
}
// 其他 CRUD 操作類似...
private void getOne(RoutingContext rc) { /* ... */ }
private void addOne(RoutingContext rc) { /* ... */ }
private void updateOne(RoutingContext rc) { /* ... */ }
private void deleteOne(RoutingContext rc) { /* ... */ }
try-with-resources 風格或 closeQuietly() 確保釋放// ✅ 正確:使用 updateWithParams / queryWithParams
conn.updateWithParams("INSERT INTO t (col) VALUES (?)", new JsonArray().add(userInput));
// ❌ 錯誤:字串拼接
conn.update("INSERT INTO t (col) VALUES ('" + userInput + "')");
jdbcClient.getConnection(ar -> {
SQLConnection conn = ar.result();
conn.setAutoCommit(false, txAr -> {
// 執行多條 SQL...
conn.commit(commitAr -> {
if (commitAr.failed()) {
conn.rollback(rollbackAr -> closeQuietly(conn));
} else {
closeQuietly(conn);
}
});
});
});
@Override public void stop(PromisestopPromise) { jdbcClient.close(stopPromise); // 關閉連接池 }
// 單元測試配置 (內存資料庫)
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"));
{
"url": "jdbc:postgresql://db-host:5432/myapp",
"driver_class": "org.postgresql.Driver",
"max_pool_size": 50,
"user": "app_user",
"password": "${DB_PASSWORD}" // 從環境讀取
}
Vert.x 異步 JDBC 讓資料庫操作不再阻塞事件循環,配合連接池與非阻塞 API,能構建高吞吐、低延遲的反應式應用。關鍵在於:掌握 Handler 模式、嚴格管理連接生命週期、善用參數化查詢。