Vert.x 异步访问数据库 MySQL
Vert.x提供异步访问数据库的API,数据库操作是一个耗时操作,使用传统的同步模型,容易阻塞线程,导致整体性能下降,因此我们对于数据库操作,需要使用Vert.x提供的异步API。
Vert.x提供的API层级非常低,可以说是仅仅在原生JDBC基础上封装了一层异步接口。所有的对数据库操作都需要通过编写SQL来完成,参数的封装和结果的获取都需要手动的来实现,对于习惯使用ORM框架的开发者可能会非常的不习惯。
先来通过一个查询数据库的案例来演示如何使用Vert.x提供的异步API
基本操作
1.引入数据库依赖,我们需要引入两个包,一个是vertx-jdbc,另一个是要真正连接数据库的驱动包,这里以MySQL为例
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-jdbc-client</artifactId>
<version>3.6.0</version>
</depend <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
2.抽象出一个DbUtils来方便获取数据库客户端,为了简单,直接就将配置写到代码里了
 public class JdbcUtils {
 // 用于操作数据库的客户端
 private JDBCClient dbClient;
 public JdbcUtils(Vertx vertx) {
 // 构造数据库的连接信息
 JsonObject dbConfig = new JsonObject();
 dbConfig.put("url", "jdbc:mysql://192.168.40.66:3306/test");
 dbConfig.put("driver_class", "com.mysql.jdbc.Driver");
 dbConfig.put("user", "xxxx");
 dbConfig.put("password", "xxxx");
 // 创建客户端
 dbClient = JDBCClient.createShared(vertx, dbConfig);
 }
 // 提供一个公共方法来获取客户端
 public JDBCClient getDbClient() {
 return dbClient;
 }
 }
通过上面的工具类,可以快速的获取到客户端,看上面的代码也很简单,通过JsonObect构建一些基本的数据库连接信息,然后通过JDBCClient的createShard方法创建一个JDBCClient实例。
3.进行数据库的操作,以查询年龄大于18岁的用户为例
 public class JdbcTestVerticle extends AbstractVerticle {
 @Override
 public void start() throws Exception {
 // 获取到数据库连接的客户端
 JDBCClient jdbcClient = new JdbcUtils(vertx).getDbClient();
 String sql = "select * from t_user where age > ?";
 // 构造参数
 JsonArray params = new JsonArray().add(18);
 // 执行查询
 jdbcClient.queryWithParams(sql, params, qryRes->{
 if(qryRes.succeeded()) {
 // 获取到查询的结果,Vert.x对ResultSet进行了封装
 ResultSet resultSet = qryRes.result();
 // 把ResultSet转为List<JsonObject>形式
 List<JsonObject> rows = resultSet.getRows();
 // 输出结果
 System.out.println(rows);
 } else {
 System.out.println("查询数据库出错!");
 }
 });
 }
 public static void main(String[] args) {
 Vertx vertx = Vertx.vertx();
 vertx.deployVerticle(new JdbcTestVerticle());
 }
 }
JsonArray是一个数组,SQL中用到的参数可以通过构建一个JsonArray来赋值。
JsonObejct是一个Json对象,类似于阿里的fastjson中提供的JSONObject
这两个对象在Vert.x中非常常用,而且非常的好用,但一定要注意空指针的问题,这是非常让人头疼的。
优化
通过上面的三个步骤,就可成功的对数据库进行操作了,但还有些问题需要优化,比如数据库连接信息放到配置文件中,再比如使用数据库连接池等等。
* 使用配置文件
 {
 "default":{
 "url":"jdbc:mysql://localhost:3306/my_project",
 "driver_class":"com.mysql.cj.jdbc.Driver",
 "user":"root",
 "password":"root"
 },
 "prod":{
 "url":"jdbc:mysql://localhost:3306/my_project",
 "driver_class":"com.mysql.cj.jdbc.Driver",
 "user":"root",
 "password":"root"
 }
 }
 修改DbUtils工具类
 public class JdbcUtils {
 private JDBCClient dbClient;
 private static JsonObject config ;
 static {
 byte[] buff = new byte[102400];
 try {
 // 读取配置文件
 InputStream ins = new FileInputStream("db.json");
 int i = IOUtils.read(ins, buff);
 config = new JsonObject(new String(buff, 0, i));
 } catch (Exception e) {
 System.out.println("读取配置文件失败");
 }
 }
 public JdbcUtils(Vertx vertx, String dsName) {
 JsonObject dbConfig = config.getJsonObject(dsName);
 if(dbConfig == null) {
 throw new RuntimeException("没有找到指定的数据源");
 }
 dbClient = JDBCClient.createShared(vertx, dbConfig);
 }
 public JdbcUtils(Vertx vertx) {
 this(vertx, "default");
 }
 public JDBCClient getDbClient() {
 return dbClient;
 }
 }
这样就支持了多个数据源,而且数据库连接配置都放到了配置文件中。
连接池配置
数据连接池默认使用的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. 设置不自动提交事务
// 开启事务
connection.setAutoCommit(false, (v) -> {
if (v.succeeded()) { }
});
3.dml操作
// 执行更新操作
connection.update("sql", upRes -> {
if(upRes.succeed()){ }
});
4. 提交事务
// 提交事务
connection.commit(rx -> {
if (rx.succeeded()) {
// 事务提交成功
}
});
回滚事务
// 回滚事务
connection.rollback(rb -> {
if (rb.succeeded()) {
// 事务回滚成功
}
});
如果你觉得上面的还很简单,看看下面一个完整的例子吧,把这些嵌套在一起,你还觉得简单吗?
package stu.vertx.jdbc; import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.SQLConnection; /**
* 获得数据库连接,执行查询,开启事务,执行更新操作
*
* @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
* @version 1.0
* @date 2019/4/3 9:19
*/
public class GetConnection extends AbstractVerticle { @Override
public void start() throws Exception { JDBCClient jdbcClient = new JdbcUtils(vertx).getDbClient();
System.out.println("获取到数据库客户端");
// 获取数据库连接
jdbcClient.getConnection(con -> {
if (con.succeeded()) {
System.out.println("获取到数据库连接"); // 获取到的连接对象
SQLConnection connection = con.result(); // 执行查询操作
connection.query("select * from t1", rs -> {
// 处理查询结果
if (rs.succeeded()) {
System.out.println(rs.result().getRows());
}
}); // 开启事务
connection.setAutoCommit(false, (v) -> {
if (v.succeeded()) {
// 事务开启成功 执行crud操作
connection.update("update t1 set name = '被修改了' where name = '111'", up -> { if (up.succeeded()) {
// 再来一笔写操作
connection.update("insert into t1 values ('222','222222') ", up2 -> {
if (up2.succeeded()) {
// 提交事务
connection.commit(rx -> {
if (rx.succeeded()) {
// 事务提交成功
}
});
} else {
connection.rollback(rb -> {
if (rb.succeeded()) {
// 事务回滚成功
}
});
}
});
} else {
connection.rollback(rb -> {
if (rb.succeeded()) {
// 事务回滚成功
}
});
}
}); } else {
System.out.println("开启事务失败");
}
});
} else {
System.out.println("获取数据库连接失败");
}
}); } public static void main(String[] args) {
Vertx.vertx().deployVerticle(new GetConnection());
}
}
RxJava解决多层回调嵌套问题
上面的代码仅仅是做了两个写操作,可以说是非常的痛苦了,一层一层的嵌套,根本没法维护。那么在真实的开发环境中,该如何管理事务呢,这就需要使用rxjava了,能够有效的减少多层嵌套带来的问题。使用rxjava首先是需要引入rxjava的依赖
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-rx-java</artifactId>
<version>3.7.0</version>
</dependency>
完成上面案例的同样代码如下
package stu.vertx.jdbc; import io.vertx.core.*;
import io.vertx.core.json.JsonArray;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.SQLConnection;
import rx.Single; import java.util.UUID; /**
* */
public class GetConnectionWithRxJava extends AbstractVerticle { @Override
public void start() throws Exception { // 获取JDBC客户端
JDBCClient jdbcClient = new JdbcUtils(vertx).getDbClient(); getConnection(jdbcClient, con -> {
if (con.succeeded()) {
// 获取到与数据库的连接
SQLConnection connection = con.result(); // 开启事务
rxOpenTx(connection)
// 执行写操作
.flatMap(this::rxExecuteUpdate1)
// 执行写操作
.flatMap(this::rxExecuteUpdate2)
.subscribe(ok -> {
// 提交事务
ok.commit(v -> {
});
}, err -> {
// 回滚事务
connection.rollback(v -> {
});
});
}
});
} public Single<SQLConnection> rxOpenTx(SQLConnection connection) {
return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> openTx(connection, fut)));
} public Single<SQLConnection> rxExecuteUpdate1(SQLConnection connection) {
return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> update1(connection, fut)));
} public Single<SQLConnection> rxExecuteUpdate2(SQLConnection connection) {
return Single.create(new io.vertx.rx.java.SingleOnSubscribeAdapter<>(fut -> update2(connection, fut)));
} public void getConnection(JDBCClient jdbcClient, Handler<AsyncResult<SQLConnection>> resultHandler) {
jdbcClient.getConnection(con -> {
if (con.succeeded()) {
resultHandler.handle(Future.succeededFuture(con.result()));
} else {
resultHandler.handle(Future.failedFuture(con.cause()));
}
});
} public void openTx(SQLConnection connection, Handler<AsyncResult<SQLConnection>> resultHandler) {
connection.setAutoCommit(false, o -> {
if (o.succeeded()) {
resultHandler.handle(Future.succeededFuture(connection));
} else {
resultHandler.handle(Future.failedFuture(o.cause()));
}
});
} public void update1(SQLConnection connection, Handler<AsyncResult<SQLConnection>> resultHandler) {
connection.updateWithParams("insert into t1 values (?,?)", new JsonArray().add(UUID.randomUUID().toString()).add(UUID.randomUUID().toString()), in -> {
if (in.succeeded()) {
resultHandler.handle(Future.succeededFuture(connection));
} else {
resultHandler.handle(Future.failedFuture(in.cause()));
}
});
} public void update2(SQLConnection connection, Handler<AsyncResult<SQLConnection>> resultHandler) {
connection.update("update t1 set name = '111' where passwd = '111'", in -> {
if (in.succeeded()) {
resultHandler.handle(Future.succeededFuture(connection));
} else {
resultHandler.handle(Future.failedFuture(in.cause()));
}
});
} public static void main(String[] args) {
Vertx.vertx().deployVerticle(new GetConnectionWithRxJava());
}
}
通过使用RxJava,没有那么深的嵌套层次,逻辑比较清晰。当然了,为了一个简单的操作,还是需要写很多的代码。
Vert.x 异步访问数据库 MySQL的更多相关文章
- 使用ab.exe监测100个并发/100次请求情况下同步/异步访问数据库的性能差异
		
ab.exe介绍 ab.exe是apache server的一个组件,用于监测并发请求,并显示监测数据 具体使用及下载地址请参考:http://www.cnblogs.com/gossip/p/439 ...
 - Java访问数据库Mysql
		
一.概述 本文主要介绍Java接连数据库的基本方法和步骤,并对其中的几个要点进行简要说明. 二.数据库访问步骤 在Java中连接数据库进行的访问主要有以下几个步骤: 加载数据库驱动 注册数据库驱动 建 ...
 - 使用Tomcat数据源的方式访问数据库(MySql) --Struts2框架应用与开发
		
1.为方便测试首先创建数据库和表,然后插入测试数据 2.打开Tomcat服务器安装目录的conf/下的context.xml,配置context.xml文件. 在<Context>标签 ...
 - springboot访问数据库(MySql)
		
1.使用JDBC访问数据库:JDBC是用于在Java语言编程中与数据库连接的API <dependency> <groupId>org.springframework.boot ...
 - Holer实现外网访问本地MySQL数据库
		
外网访问内网MySQL数据库 内网主机上安装了MySQL数据库,只能在局域网内访问,怎样从公网也能访问本地MySQL数据库? 本文将介绍使用holer实现的具体步骤. 1. 准备工作 1.1 安装并启 ...
 - 【MySql 】is not allowed to connect to this MySql server 无法访问远程MySQL数据库
		
问题:访问远程MySQL数据库时报错[is not allowed to connect to this MySql server ]不允许访问,因为MySQL默认只有本机localhost能够访问M ...
 - 访问远程mysql数据库,出现报错,显示“1130 - Host'xxx.xxx.xxx.xxx' is not allowed to connect to this MySQL server“
		
在使用Navicat for MySQl访问远程mysql数据库,出现报错,显示“1130 - Host'xxx.xxx.xxx.xxx' is not allowed to connect to t ...
 - 数据库安装后无法访问且mysql重启报错的解决方法
		
数据库安装后无法访问,mysql重启报错: 或报错:MySQL is running but PID file could not be found 解决方法: 第一种方法:看磁盘是否已满:df –h ...
 - JDBC访问数据库的具体步骤(MySql  + Oracle + SQLServer)
		
* 感谢DT课堂颜群老师的视频讲解(讲的十分仔细,文末有视频链接) import java.sql.Connection; import java.sql.DriverManager; import ...
 
随机推荐
- myeclipse的安装与破解
			
myeclipe安装和破解一直困扰我很长时间,我又是尴尬症的人,不破解就是不行,花费一天时间终于搞定是怎么破解的. 一:首先myeclipse的官方下载网站www.myeclipsecn.com/do ...
 - 创建第一个react项目
			
前提:已安装部署好nodejs环境 查看nodejs是否安装以及版本 1,win+r输入cmd打开命令行页面 2,路径换到nodejs目录 3,输入node -v查看版本 创建项目 1,win+r输入 ...
 - 第08组 Alpha冲刺(4/4)
			
小李的博客 作业博客 作业链接 组员1李昕晖(组长) 过去两天完成了哪些任务 文字/口头描述 11月20日了解各个小组的进度与难以攻破的地方,晚上安排开会,安排新的冲刺任务. 实现地图功能 展示Git ...
 - Python3爬取美女妹子图片转载
			
# -*- coding: utf-8 -*- """ Created on Sun Dec 30 15:38:25 2018 @author: 球球 "&qu ...
 - 【java编程】vo、po、dto、bo、pojo、entity、mode如何区分
			
Java Bean:一种可重用组件,即“一次编写,任何地方执行,任何地方重用”.满足三个条件 类必须是具体的和公共的 具有无参构造器 提供一致性设计模式的公共方法将内部域或暴露成员属性 VO valu ...
 - 配置IDEA项目JDK环境
			
打开IDEA,然后点击[Configure]->[Project Defaults]->[Project Structure],如下图: 然后左侧点击树形菜单的[Project Sett ...
 - Android Camera2/HAL3
			
Android : Camera2/HAL3 框架分析 https://www.cnblogs.com/blogs-of-lxl/p/10651611.html Android : Camera之ca ...
 - StarGAN: Unified Generative Adversarial Networks for Multi-Domain Image-to-Image Translation - 1 - 多个域间的图像翻译论文学习
			
Abstract 最近在两个领域上的图像翻译研究取得了显著的成果.但是在处理多于两个领域的问题上,现存的方法在尺度和鲁棒性上还是有所欠缺,因为需要为每个图像域对单独训练不同的模型.为了解决该问题,我们 ...
 - WebGL学习笔记(十二):加载模型文件
			
目前为止,我们用到的模型顶点uv信息等,都是直接定义在代码中的,实际使用中,这些数据应该是由3D编辑器编辑好后按照一定的格式存储在文件中的,我们需要从文件中提取出对应的数据之后,组合成我们可以使用的信 ...
 - cps在jenkins构建报错
			
修改ares的版本号即可,改为2.0.1.14-20191126-RELEASE