• 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,没有那么深的嵌套层次,逻辑比较清晰。当然了,为了一个简单的操作,还是需要写很多的代码。 

  • 相关阅读:
    04:求整数的和与均值
    03:均值
    02:财务管理
    C8-3 三角形还是长方形? (100/100 分数)
    C8-2 圆的周长和面积 (100/100 分数)
    C8-1 复数加减乘除 (100/100 分数)
    C7-3 用类实现a+b (100/100 分数)
    C7-2 多继承 (100/100 分数)
    C7-1 账户类(100/100)
    数组第k小数
  • 原文地址:https://www.cnblogs.com/endv/p/11247947.html
Copyright © 2020-2023  润新知