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 }

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

原文地址:https://www.cnblogs.com/endv/p/11247947.html

时间: 2024-08-29 10:30:12

Vert.x 异步访问数据库 MySQL的相关文章

使用ab.exe监测100个并发/100次请求情况下同步/异步访问数据库的性能差异

ab.exe介绍 ab.exe是apache server的一个组件,用于监测并发请求,并显示监测数据 具体使用及下载地址请参考:http://www.cnblogs.com/gossip/p/4398784.html 本文的目的 通过webapi接口模拟100个并发请求下,同步和异步访问数据库的性能差异 创建数据库及数据 --创建表结构 CREATE TABLE dbo.[Cars] ( Id INT IDENTITY(1000,1) NOT NULL, Model NVARCHAR(50) 

Java访问数据库Mysql

一.概述 本文主要介绍Java接连数据库的基本方法和步骤,并对其中的几个要点进行简要说明. 二.数据库访问步骤 在Java中连接数据库进行的访问主要有以下几个步骤: 加载数据库驱动 注册数据库驱动 建立到数据库的连接 访问数据库 首先,要调用Class.ForName()加载并注册mysql驱动程序类,加载驱动程序驱动类后,需要注册驱动程序类的一个实例,DriverManager类负责管理驱动程序,这个类提供了registerDriver()方法来注册驱动程序类的实例,并且我们不需要亲自调用这个

数据库选型之亿级数据量并发访问(MySQL集群)

刘 勇  Email:[email protected] 简介 针对实际应用中并发访问MySQL的场景,本文采用多线程对MySQL进行并发读取访问,其中以返回用户所需的数据并显示在终端为测试结束节点,即将数据从MySQL集群读取后存储于客户端本地内存中.测试过程如下:分别针对4种应用场景,从10.20.50.100个线程对MySQL展开测试.测试结果表明:对场景1)一般的并发访问能够满足需求:对于场景2)和3)响应时间在分钟级,分别处于1-3分钟和10分钟左右:对于场景4)则经常会抛出异常,并且

centos安装LAMP环境时访问本地MYSQL数据库错误

近日在安装LAMP环境时访问本地MYSQL数据库错误 错误如下: Warning: mysqli::mysqli(): (HY000/2002): Can't connect to local MySQL server 问题描述: 由于将安装mysql服务器文件位置修改在home目录下,造成本地访问问题. 在网上找了很久终于找到解决问题的方法: 1.先确保在本机上使用命令能登陆,如果也出现提示sock文件位置问题,那么需要修改mysql配置文件(/etc/my.cnf) 增加如下配置: [mys

如果在本地搭一个服务器和mysql数据库环境,如果使用java来访问数据库

我们可以使用speedamp来搭一个服务器环境,可以在http://download.csdn.net/detail/baidu_nod/7630265下载 解压后无需安装直接可以使用,点击SpeedAMP.exe,然后可以通过http://localhost/phpmyadmin/index.php来访问本地数据库 java可以通过jdbc来访问数据库,比较重要的是下载一个mysql-connector-java-5.1.22-bin.jar文件,然后在java工程中引用, 在代码里可以这样来

命令行访问远程mysql数据库

命令: mysql -u用户名 -p密码 -h远程数据库IP地址 -P端口 -D数据库名 例子: 使用用root帐号,密码为123456,登录ip地址为192.168.1.110,端口为3306,名称为test的mysql数据库 mysql -uroot -p123456 -h192.168.1.110 -P3306 -Dtest

趁一切还来得及【五】数据库MySQL主从同步的实践部署

自笑栖迟淮海客,十年心事一灯前.                                           --[元]萨都拉 第二章 数据库MySQL主从同步部署操作实践 2.1 数据同步相关应用基础 ①MySQL主从同步架构是目前使用最多的数据库架构之一,尤其是负载比较大的网站,因此对于主从同步的部署管理也就显得非常重要. ②MySQL主从同步的作用:(1) 数据分布,(2) 负载平衡(load balancing),(3) 备份,(4) 高可用性(high availability

javaweb三、JDBC访问数据库

JDBC是J2SE的内容,是由java提供的访问数据库的接口,但没有提供具体的实现方法,需要数据库厂商提供,就是对应的数据库驱动. 这样的好处是可以方便的更换数据库,提高了扩展性.这也是面向接口编程的一个优点. 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.sql.St

趁一切还来得及【六】数据库MySQL读写分离与主主同步

相思相见知何日?此时此夜难为情.                                                      --[唐]李白 第一章 数据库MySQL主从复制读写分离授权 1.1 主从复制读写分离方案简单分析 ①数据库主从复制搭建之后,因为数据是单向的,因此默认规则就是所有的数据(主从相关收据)写入和更新都在主库上进行操作,避免主从同步的时候造成冲突. ②严格上来讲,从库上的非同步的库写入数据,只要和主库没有关系,也是可以写入的(或者作为主库),但是如果主从都想其中