zoukankan      html  css  js  c++  java
  • Vert.x 异步访问数据库 MySQL

    Vert.x提供异步访问数据库的API,数据库操作是一个耗时操作,使用传统的同步模型,容易阻塞线程,导致整体性能下降,因此我们对于数据库操作,需要使用Vert.x提供的异步API。

    Vert.x提供的API层级非常低,可以说是仅仅在原生JDBC基础上封装了一层异步接口。所有的对数据库操作都需要通过编写SQL来完成,参数的封装和结果的获取都需要手动的来实现,对于习惯使用ORM框架的开发者可能会非常的不习惯。

    先来通过一个查询数据库的案例来演示如何使用Vert.x提供的异步API

    基本操作
    1.引入数据库依赖,我们需要引入两个包,一个是vertx-jdbc,另一个是要真正连接数据库的驱动包,这里以MySQL为例

     1 <dependency>
     2 <groupId>io.vertx</groupId>
     3 <artifactId>vertx-jdbc-client</artifactId>
     4 <version>3.6.0</version>
     5 </depend
     6 
     7 <dependency>
     8 <groupId>mysql</groupId>
     9 <artifactId>mysql-connector-java</artifactId>
    10 <version>8.0.13</version>
    11 </dependency>

    2.抽象出一个DbUtils来方便获取数据库客户端,为了简单,直接就将配置写到代码里了

     1 public class JdbcUtils {
     2 
     3 // 用于操作数据库的客户端
     4 private JDBCClient dbClient;
     5 
     6 public JdbcUtils(Vertx vertx) {
     7 
     8 // 构造数据库的连接信息
     9 JsonObject dbConfig = new JsonObject();
    10 dbConfig.put("url", "jdbc:mysql://192.168.40.66:3306/test");
    11 dbConfig.put("driver_class", "com.mysql.jdbc.Driver");
    12 dbConfig.put("user", "xxxx");
    13 dbConfig.put("password", "xxxx");
    14 
    15 // 创建客户端
    16 dbClient = JDBCClient.createShared(vertx, dbConfig);
    17 }
    18 
    19 // 提供一个公共方法来获取客户端
    20 public JDBCClient getDbClient() {
    21 return dbClient;
    22 }
    23 
    24 }

    通过上面的工具类,可以快速的获取到客户端,看上面的代码也很简单,通过JsonObect构建一些基本的数据库连接信息,然后通过JDBCClient的createShard方法创建一个JDBCClient实例。

    3.进行数据库的操作,以查询年龄大于18岁的用户为例 

     1 public class JdbcTestVerticle extends AbstractVerticle {
     2 
     3 @Override
     4 public void start() throws Exception {
     5 
     6 // 获取到数据库连接的客户端
     7 JDBCClient jdbcClient = new JdbcUtils(vertx).getDbClient();
     8 String sql = "select * from t_user where age > ?";
     9 // 构造参数
    10 JsonArray params = new JsonArray().add(18);
    11 // 执行查询
    12 jdbcClient.queryWithParams(sql, params, qryRes->{
    13 if(qryRes.succeeded()) {
    14 // 获取到查询的结果,Vert.x对ResultSet进行了封装
    15 ResultSet resultSet = qryRes.result();
    16 // 把ResultSet转为List<JsonObject>形式
    17 List<JsonObject> rows = resultSet.getRows();
    18 // 输出结果
    19 System.out.println(rows);
    20 } else {
    21 System.out.println("查询数据库出错!");
    22 }
    23 });
    24 
    25 }
    26 
    27 public static void main(String[] args) {
    28 Vertx vertx = Vertx.vertx();
    29 vertx.deployVerticle(new JdbcTestVerticle());
    30 }
    31 }

    JsonArray是一个数组,SQL中用到的参数可以通过构建一个JsonArray来赋值。

    JsonObejct是一个Json对象,类似于阿里的fastjson中提供的JSONObject

    这两个对象在Vert.x中非常常用,而且非常的好用,但一定要注意空指针的问题,这是非常让人头疼的。

    优化

    通过上面的三个步骤,就可成功的对数据库进行操作了,但还有些问题需要优化,比如数据库连接信息放到配置文件中,再比如使用数据库连接池等等。

    * 使用配置文件

     1 {
     2 "default":{
     3 "url":"jdbc:mysql://localhost:3306/my_project",
     4 "driver_class":"com.mysql.cj.jdbc.Driver",
     5 "user":"root",
     6 "password":"root"
     7 },
     8 "prod":{
     9 "url":"jdbc:mysql://localhost:3306/my_project",
    10 "driver_class":"com.mysql.cj.jdbc.Driver",
    11 "user":"root",
    12 "password":"root"
    13 }
    14 }
    15 修改DbUtils工具类
    16 
    17 public class JdbcUtils {
    18 
    19 private JDBCClient dbClient;
    20 private static JsonObject config ;
    21 
    22 static {
    23 byte[] buff = new byte[102400];
    24 try {
    25 // 读取配置文件
    26 InputStream ins = new FileInputStream("db.json");
    27 int i = IOUtils.read(ins, buff);
    28 config = new JsonObject(new String(buff, 0, i));
    29 } catch (Exception e) {
    30 System.out.println("读取配置文件失败");
    31 }
    32 }
    33 
    34 public JdbcUtils(Vertx vertx, String dsName) {
    35 JsonObject dbConfig = config.getJsonObject(dsName);
    36 if(dbConfig == null) {
    37 throw new RuntimeException("没有找到指定的数据源");
    38 }
    39 dbClient = JDBCClient.createShared(vertx, dbConfig);
    40 }
    41 
    42 public JdbcUtils(Vertx vertx) {
    43 this(vertx, "default");
    44 }
    45 
    46 public JDBCClient getDbClient() {
    47 return dbClient;
    48 }
    49 
    50 }

    这样就支持了多个数据源,而且数据库连接配置都放到了配置文件中。

    连接池配置
    数据连接池默认使用的C3P0,所以可以在db.json中进行配置C3P0连接池的参数就可以了,这里官网的地址为:https://vertx.io/docs/vertx-jdbc-client/java/

    具体配置可以参考官网给出的配置,下面是一个简单的截图

    遗憾的是,Vert.x给出的数据库连接池的支持并不多,如果我们想要使用比如阿里的Druid连接池,需要自己来实现DataSourceProvider。当然DataSourceProvider的实现并不复杂,但麻烦啊!后面我会给出一个关于druid的DataSourceProvider的实现。

    事务
    Vert.x从比较低的层面来控制事务,不像Spring一样可以使用声明式事务管理。要想在Vert.x中开启事务,和传统的JDBC管理事务的方式非常类似。首先要获得到连接,然后调用连接的setAutoCommit方法,关闭事务的自动提交,然后再手动的提交和回滚事务。

    因为开启事务、提交事务、执行SQL都需要和数据库服务进行通信,因此在Vert.x中都是异步操作,按传统方式实现一个事务代码非常痛苦,看下面的一段开启事务的代码。写了一遍以后,绝对不愿意再写第二遍。

    1. 获得连接

    // 获得连接
    jdbcClient.getConnection(con -> {
    if (con.succeeded()) {
    System.out.println("获取到数据库连接");
    
    // 获取到的连接对象
    SQLConnection connection = con.result();
    }
    });

    2. 设置不自动提交事务

    1 // 开启事务
    2 connection.setAutoCommit(false, (v) -> {
    3 if (v.succeeded()) {
    4 
    5 }
    6 });

    3.dml操作

    1 // 执行更新操作
    2 connection.update("sql", upRes -> {
    3 if(upRes.succeed()){
    4 
    5 }
    6 });

    4. 提交事务

    1 // 提交事务
    2 connection.commit(rx -> {
    3 if (rx.succeeded()) {
    4 // 事务提交成功
    5 }
    6 });

     回滚事务

    1 // 回滚事务
    2 connection.rollback(rb -> {
    3 if (rb.succeeded()) {
    4 // 事务回滚成功
    5 }
    6 });

    如果你觉得上面的还很简单,看看下面一个完整的例子吧,把这些嵌套在一起,你还觉得简单吗?

     1 package stu.vertx.jdbc;
     2 
     3 import io.vertx.core.AbstractVerticle;
     4 import io.vertx.core.Vertx;
     5 import io.vertx.ext.jdbc.JDBCClient;
     6 import io.vertx.ext.sql.SQLConnection;
     7 
     8 /**
     9 * 获得数据库连接,执行查询,开启事务,执行更新操作
    10 *
    11 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
    12 * @version 1.0
    13 * @date 2019/4/3 9:19
    14 */
    15 public class GetConnection extends AbstractVerticle {
    16 
    17 @Override
    18 public void start() throws Exception {
    19 
    20 JDBCClient jdbcClient = new JdbcUtils(vertx).getDbClient();
    21 System.out.println("获取到数据库客户端");
    22 // 获取数据库连接
    23 jdbcClient.getConnection(con -> {
    24 if (con.succeeded()) {
    25 System.out.println("获取到数据库连接");
    26 
    27 // 获取到的连接对象
    28 SQLConnection connection = con.result();
    29 
    30 // 执行查询操作
    31 connection.query("select * from t1", rs -> {
    32 // 处理查询结果
    33 if (rs.succeeded()) {
    34 System.out.println(rs.result().getRows());
    35 }
    36 });
    37 
    38 // 开启事务
    39 connection.setAutoCommit(false, (v) -> {
    40 if (v.succeeded()) {
    41 // 事务开启成功 执行crud操作
    42 connection.update("update t1 set name = '被修改了' where name = '111'", up -> {
    43 
    44 if (up.succeeded()) {
    45 // 再来一笔写操作
    46 connection.update("insert into t1 values ('222','222222') ", up2 -> {
    47 if (up2.succeeded()) {
    48 // 提交事务
    49 connection.commit(rx -> {
    50 if (rx.succeeded()) {
    51 // 事务提交成功
    52 }
    53 });
    54 } else {
    55 connection.rollback(rb -> {
    56 if (rb.succeeded()) {
    57 // 事务回滚成功
    58 }
    59 });
    60 }
    61 });
    62 } else {
    63 connection.rollback(rb -> {
    64 if (rb.succeeded()) {
    65 // 事务回滚成功
    66 }
    67 });
    68 }
    69 });
    70 
    71 } else {
    72 System.out.println("开启事务失败");
    73 }
    74 });
    75 } else {
    76 System.out.println("获取数据库连接失败");
    77 }
    78 });
    79 
    80 
    81 }
    82 
    83 public static void main(String[] args) {
    84 Vertx.vertx().deployVerticle(new GetConnection());
    85 }
    86 }
    View Code

    RxJava解决多层回调嵌套问题

    上面的代码仅仅是做了两个写操作,可以说是非常的痛苦了,一层一层的嵌套,根本没法维护。那么在真实的开发环境中,该如何管理事务呢,这就需要使用rxjava了,能够有效的减少多层嵌套带来的问题。使用rxjava首先是需要引入rxjava的依赖

    1 <dependency>
    2 <groupId>io.vertx</groupId>
    3 <artifactId>vertx-rx-java</artifactId>
    4 <version>3.7.0</version>
    5 </dependency>

    完成上面案例的同样代码如下

      1 package stu.vertx.jdbc;
      2 
      3 import io.vertx.core.*;
      4 import io.vertx.core.json.JsonArray;
      5 import io.vertx.ext.jdbc.JDBCClient;
      6 import io.vertx.ext.sql.SQLConnection;
      7 import rx.Single;
      8 
      9 import java.util.UUID;
     10 
     11 /**
     12 *  15 */
     16 public class GetConnectionWithRxJava extends AbstractVerticle {
     17 
     18 @Override
     19 public void start() throws Exception {
     20 
     21 // 获取JDBC客户端
     22 JDBCClient jdbcClient = new JdbcUtils(vertx).getDbClient();
     23 
     24 getConnection(jdbcClient, con -> {
     25 if (con.succeeded()) {
     26 // 获取到与数据库的连接
     27 SQLConnection connection = con.result();
     28 
     29 // 开启事务
     30 rxOpenTx(connection)
     31 // 执行写操作
     32 .flatMap(this::rxExecuteUpdate1)
     33 // 执行写操作
     34 .flatMap(this::rxExecuteUpdate2)
     35 .subscribe(ok -> {
     36 // 提交事务
     37 ok.commit(v -> {
     38 });
     39 }, err -> {
     40 // 回滚事务
     41 connection.rollback(v -> {
     42 });
     43 });
     44 }
     45 });
     46 }
     47 
     48 public Single<SQLConnection> rxOpenTx(SQLConnection connection) {
     49 return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> openTx(connection, fut)));
     50 }
     51 
     52 public Single<SQLConnection> rxExecuteUpdate1(SQLConnection connection) {
     53 return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> update1(connection, fut)));
     54 }
     55 
     56 public Single<SQLConnection> rxExecuteUpdate2(SQLConnection connection) {
     57 return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> update2(connection, fut)));
     58 }
     59 
     60 public void getConnection(JDBCClient jdbcClient, Handler<AsyncResult<SQLConnection>> resultHandler) {
     61 jdbcClient.getConnection(con -> {
     62 if (con.succeeded()) {
     63 resultHandler.handle(Future.succeededFuture(con.result()));
     64 } else {
     65 resultHandler.handle(Future.failedFuture(con.cause()));
     66 }
     67 });
     68 }
     69 
     70 public void openTx(SQLConnection connection, Handler<AsyncResult<SQLConnection>> resultHandler) {
     71 connection.setAutoCommit(false, o -> {
     72 if (o.succeeded()) {
     73 resultHandler.handle(Future.succeededFuture(connection));
     74 } else {
     75 resultHandler.handle(Future.failedFuture(o.cause()));
     76 }
     77 });
     78 }
     79 
     80 public void update1(SQLConnection connection, Handler<AsyncResult<SQLConnection>> resultHandler) {
     81 connection.updateWithParams("insert into t1 values (?,?)", new JsonArray().add(UUID.randomUUID().toString()).add(UUID.randomUUID().toString()), in -> {
     82 if (in.succeeded()) {
     83 resultHandler.handle(Future.succeededFuture(connection));
     84 } else {
     85 resultHandler.handle(Future.failedFuture(in.cause()));
     86 }
     87 });
     88 }
     89 
     90 public void update2(SQLConnection connection, Handler<AsyncResult<SQLConnection>> resultHandler) {
     91 connection.update("update t1 set name = '111' where passwd = '111'", in -> {
     92 if (in.succeeded()) {
     93 resultHandler.handle(Future.succeededFuture(connection));
     94 } else {
     95 resultHandler.handle(Future.failedFuture(in.cause()));
     96 }
     97 });
     98 }
     99 
    100 public static void main(String[] args) {
    101 Vertx.vertx().deployVerticle(new GetConnectionWithRxJava());
    102 }
    103 }

    通过使用RxJava,没有那么深的嵌套层次,逻辑比较清晰。当然了,为了一个简单的操作,还是需要写很多的代码。 

  • 相关阅读:
    easyExcel入门
    UML-从需求到设计--迭代进化
    UML-操作契约总结
    102. Binary Tree Level Order Traversal
    98. Validate Binary Search Tree
    95. Unique Binary Search Trees II
    96. Unique Binary Search Trees
    94. Binary Tree Inorder Traversal
    84. Largest Rectangle in Histogram
    92. Reverse Linked List II
  • 原文地址:https://www.cnblogs.com/endv/p/11247947.html
Copyright © 2011-2022 走看看