那篇作品的代码在,API进行编制程序

异步JDBC

看了一部分着力的异步的API,现在掌握下vertx-jdbc-client。那个组件能够让大家经过JDBC driver与数据库交互。那些交互都以异步的,在此以前那样:

String sql = "SELECT * FROM Products";
ResultSet rs = stmt.executeQuery(sql);

明日要那样:

connection.query("SELECT * FROM Products", result -> {
        // do something with the result
});

其一模型更便捷,当结果出来后vert.x公告,制止了等候结果。

Java JDBC下执行SQL的不等措施、参数化预编译防御 

原文**  http://www.cnblogs.com/LittleHann/p/3695332.html**

目录

1. Java JDBC简介
2. Java JDBC下执行SQL的不同方式
3. Java JDBC编程实践
  1. Java JDBC简介

数据库驱动程序是JDBC程序和数据库之间的转换层,数据库驱动程序负责将JDBC调用映射成特定的数据库调用,类似PHP中的”数据库抽象层”

http://www.php.net/manual/zh/refs.database.vendors.php

应用Java JDBC
API进行编制程序,能够为多样关周密据库提供联合访问,为大家带来跨平台、跨数据库系统的好处

总的来说,JDBC驱动平时有如下4种档次

1. JDBC-ODPC桥:
它将JDBC API映射到ODPC API。再让JDBC-ODPC调用数据库本地驱动代码(也就是数据库厂商提供的数据库操作二进制代码库,例如Oracle中的oci.dll)
2. 本地API驱动
直接将JDBC API映射成数据库特定的客户端API,即通过客户端加载数据库厂商提供的本地代码库(C/C++等) 
3. 网络协议驱动
这种类型的驱动给客户端提供了一个网络API,客户端上的JDBC驱动程序使用套接字(Socket)来调用服务器上的中间件程序,后者在将其请求转化为所需的具体API调用。
4. 本地协议驱动
这种类型的驱动使用Socket,直接在客户端和数据库间通信。它是一种直接与数据库实例交互的JDBC
这种驱动是智能的,它知道数据库使用的底层协议,也是目前最主流使用的JDBC驱动,我们本章的重点就是它

JDBC编制程序(连接数据库)步骤

1) 加载数据库驱动
通常我们使用Class类的forName()静态方法来加载驱动(由各个数据库厂商自己实现)
Class.forName("com.mysql.jdbc.Driver");
Class.forName("oracle.jdbc.driver.OracleDriver");
"com.mysql.jdbc.Driver"、"oracle.jdbc.driver.OracleDriver"代表了数据库驱动类对应的字符串
2) 通过DriverManager获取数据库连接
DriverManager.getConnection(String url, String user, String pass);
  2.1) url: 数据库连接字符串
    2.1.1) Mysql
    jdbc:mysql://hostname:port/databasename
    2.1.2) Oracle
    jdbc:oracle:thin:@hostname:port:databasename
  2.2) user: 数据库的系统用户名
  2.3) pass: 数据库的系统密码
3) 通过Connection对象创建Statement对象,Connection创建Statement对象的方法有如下3个
  3.1) createStatement(String sql):创建基本的Statement对象
  3.2) prepareStatement(String sql): 根据传入的SQL语句创建预编译的Statement对象
  3.3) prepareCall(String sql): 根据传入的SQL语句创建CallableStatement对象
4) 使用Statement执行SQL语句
所有的Statement都有如下3个方法来执行SQL语句
  4.1) execute(): 可以执行任何SQL语句,但比较麻烦
  4.2) executeUpdate(): 主要用于执行DML和DDL语句。执行DML语句返回受SQL影响的行数,执行DDL语句返回0
  4.3) executeQuery(): 只能执行查询语句,执行后返回代表查询结果的ResultSet对象
5) 操作结果集
如果执行的SQL语句是查询语句,则执行结果将返回一个ResultSet对象,该对象里保存了SQL语句查询的结果。程序可以通过操作该ResultSet对象来取出查询结果。ResultSet对象主要提供了如
下方法
  5.1) 移动记录指针的方法
    5.1.1) next()
    5.1.2) previous()
    5.1.3) first()
    5.1.4) last()
    5.1.5) beforeFirst()
    5.1.6) afterLast()
    5.1.7) absolute()
  5.2) 获取指针指向的某行的"特定的列值"
    5.2.1) getInt()
     5.2.2) getString()
     ...
    该方法既可以使用列索引作为参数,也可以使用列名作为参数
6) 回收数据库资源
包括关闭ResultSet、Statement、Connection等资源
  1. Java JDBC下执行SQL的两样形式

JDBC是1个对峙比较底层的API接口,它只提须要大家一个进行原生SQL语句的输入接口。所以和JDBC的API接口都定义在

java.sql
javax.sql

我们理解,Java JDBC编程中有3种办法开始展览DDL、DML语句的施行

1. createStatement
返回Statement
  1) execute() 
  2) executeUpdate()
  3) executeQuery()
2. prepareStatement
返回PrepareStatement
  1) execute() 
  2) executeUpdate()
  3) executeQuery()
3. prepareCall
返回CallableStatement

咱俩来挨家挨户学习

0x1: 基本的Statement对象

Statement提供了三个法子来执行SQL语句,它们都足以用来执行DDL(执行后重临值为0)、DML(执行后归来受影响行数)语句

使用execute执行DDL、DML语句

Statement的execute()方法大致能够实施其余原生SQL语句,但它执行SQL语句时相比较费心(它不可能直接回到一个ResultSet,而是供给大家手工业再度得到),平常情状下,使用executeQuery()、或executeUpdate()方法特别简约。但万一程序员不领悟SQL语句的花色,则不得不使用execute()方法来推行该SQL语句了。

专注,使用execute()方法执行SQL语句的再次回到值”只是”boolean值,它标志了履行该SQL语句是不是重回了ResultSet对象,而小编辈要博取实际的ResultSet对象还索要非常的点子

1. getResult(): 获取该Statement执行查询语句所返回的ResultSet对象
2. getUpdateCount(): 获取该Statement执行DML语句所影响的记录行数

code:

import java.util.*;
import java.io.*;
import java.sql.*;

public class ExecuteSQL
{
  private String driver;
  private String url;
  private String user;
  private String pass;
  public void initParam(String paramFile)throws Exception
  {
    // 使用Properties类来加载属性文件
    Properties props = new Properties();
    props.load(new FileInputStream(paramFile));
    driver = props.getProperty("driver");
    url = props.getProperty("url");
    user = props.getProperty("user");
    pass = props.getProperty("pass");
  }
  public void executeSql(String sql)throws Exception
  {
    // 加载驱动
    Class.forName(driver);
    try(
      // 获取数据库连接
      Connection conn = DriverManager.getConnection(url
        , user , pass);
      // 使用Connection来创建一个Statement对象
      Statement stmt = conn.createStatement())
    {
      // 执行SQL,返回boolean值表示是否包含ResultSet
      boolean hasResultSet = stmt.execute(sql);
      // 如果执行后有ResultSet结果集
      if (hasResultSet)
      {
        try(
          // 获取结果集
          ResultSet rs = stmt.getResultSet())
        {
          // ResultSetMetaData是用于分析结果集的元数据接口
          ResultSetMetaData rsmd = rs.getMetaData();
          int columnCount = rsmd.getColumnCount();
          // 迭代输出ResultSet对象
          while (rs.next())
          {
            // 依次输出每列的值
            for (int i = 0 ; i < columnCount ; i++ )
            {
              System.out.print(rs.getString(i + 1) + "\t");
            }
            System.out.print("\n");
          }
        }
      }
      else
      {
        System.out.println("该SQL语句影响的记录有" 
          + stmt.getUpdateCount() + "条");
      }
    }
  }
  public static void main(String[] args) throws Exception
  {
    ExecuteSQL es = new ExecuteSQL();
    es.initParam("mysql.ini");
    System.out.println("------执行删除表的DDL语句-----");
    es.executeSql("drop table if exists my_test");
    System.out.println("------执行建表的DDL语句-----");
    es.executeSql("create table my_test"
    + "(test_id int auto_increment primary key, "
    + "test_name varchar(255))");
    System.out.println("------执行插入数据的DML语句-----");
    es.executeSql("insert into my_test(test_name) "
    + "select student_name from student_table");
    System.out.println("------执行查询数据的查询语句-----");
    es.executeSql("select * from my_test");
  }
}

使用executeUpdate执行DDL、DML语句

import java.util.*;
import java.io.*;
import java.sql.*;

public class ExecuteDDL
{
  private String driver;
  private String url;
  private String user;
  private String pass;
  public void initParam(String paramFile) throws Exception
  {
    // 使用Properties类来加载属性文件
    Properties props = new Properties();
    props.load(new FileInputStream(paramFile));
    driver = props.getProperty("driver");
    url = props.getProperty("url");
    user = props.getProperty("user");
    pass = props.getProperty("pass");
  }
  public void createTable(String sql)throws Exception
  {
    // 加载驱动
    Class.forName(driver);
    try(
    // 获取数据库连接
    Connection conn = DriverManager.getConnection(url , user , pass);
    // 使用Connection来创建一个Statment对象
    Statement stmt = conn.createStatement())
    {
      // 执行DDL,创建数据表
      stmt.executeUpdate(sql);
    }
  }
  public static void main(String[] args) throws Exception
  {
    ExecuteDDL ed = new ExecuteDDL();
    ed.initParam("mysql.ini");
    ed.createTable("create table jdbc_test "
      + "( jdbc_id int auto_increment primary key, " 
      + "jdbc_name varchar(255), "
      + "jdbc_desc text);");
    System.out.println("-----建表成功-----");
  }
}

从代码中我们得以看到,大家并没有把数据库连接音信写在程序里,而是利用一个mysql.ini文件来保存数据库连接音信,那样当须求把程序从支付环境移植到生产条件时,无须修改源代码,只供给修改mysql.ini配置文件即可

mysql.ini:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/select_test
user=root
pass=111

使用executeQuery执行DDL、DML语句

import java.sql.*;

public class ConnMySql
{
  public static void main(String[] args) throws Exception
  {
    // 1.加载驱动,使用反射的知识,现在记住这么写。
    Class.forName("com.mysql.jdbc.Driver");
    try(
      // 2.使用DriverManager获取数据库连接,
      // 其中返回的Connection就代表了Java程序和数据库的连接
      // 不同数据库的URL写法需要查驱动文档知道,用户名、密码由DBA分配
      Connection conn = DriverManager.getConnection(
        "jdbc:mysql://127.0.0.1:3306/company"
        , "root" , "111");
      // 3.使用Connection来创建一个Statment对象
      Statement stmt = conn.createStatement();
      // 4.执行SQL语句
      /*
      Statement有三种执行sql语句的方法:
      1 execute 可执行任何SQL语句。- 返回一个boolean值,
        如果执行后第一个结果是ResultSet,则返回true,否则返回false
      2 executeQuery 执行Select语句 - 返回查询到的结果集
      3 executeUpdate 用于执行DML语句。- 返回一个整数,
        代表被SQL语句影响的记录条数
      */
      ResultSet rs = stmt.executeQuery("select *"
        + " from p8_ad_user"))
    {
      // ResultSet有系列的getXxx(列索引 | 列名),用于获取记录指针
      // 指向行、特定列的值,不断地使用next()将记录指针下移一行,
      // 如果移动之后记录指针依然指向有效行,则next()方法返回true。
      while(rs.next())
      {
        System.out.println(rs.getInt(1) + "\t"
          + rs.getString(2) + "\t"
          + rs.getString(3) + "\t"
          + rs.getString(4));
      }
    }
  }
}

0x2: prepareStatement预编写翻译对象

JDBC中的那么些类对于莱芜职员、和黑客都亟待重视关怀,它不只好够提供SQL执行质量,同时还有防卫SQL注入的作用。

当我们的工作层中和数据库相关的代码通常要实施一些相似度很高的SQL语句,它们的结果基本相似,只是插入时插入的值不一致而已

insert into student_table values(null, "LittleHann", 1);
insert into student_table values(null, "LittleHann", 2);

在那种景色下,大家得以选择带占位符(?)参数的SQL语句来代替它

insert into student_table values(null, ?, ?);

为了满足那种功能,JDBC提供了prepareStatement接口,它是Statement接口的子接口,它能够预编译SQL语句,预编写翻译后的SQL语句被贮存在prepareStatement对象中,然后能够利用该对象往往急迅地执行该语句。prepareStatement同样也使用execute()、executeUpdate()、executeQuery()来执行SQL语句,但那八个章程毫无参数,因为prepareStatement在开立的时候已经储存了预编写翻译的SQL语句,在推行SQL语句的时候假若传入参数值即可(setXxx)

Statement和prepareStatement的性质比较

import java.util.*;
import java.io.*;
import java.sql.*;

public class PreparedStatementTest
{
  private String driver;
  private String url;
  private String user;
  private String pass;
  public void initParam(String paramFile)throws Exception
  {
    // 使用Properties类来加载属性文件
    Properties props = new Properties();
    props.load(new FileInputStream(paramFile));
    driver = props.getProperty("driver");
    url = props.getProperty("url");
    user = props.getProperty("user");
    pass = props.getProperty("pass");
    // 加载驱动
    Class.forName(driver);
  }
  public void insertUseStatement()throws Exception
  {
    long start = System.currentTimeMillis();
    try(
      // 获取数据库连接
      Connection conn = DriverManager.getConnection(url
        , user , pass);
      // 使用Connection来创建一个Statment对象
      Statement stmt = conn.createStatement())
    {
      // 需要使用100条SQL语句来插入100条记录
      for (int i = 0; i < 1000 ; i++ )
      {
        stmt.executeUpdate("insert into student_table values("
          + " null ,'姓名" + i + "' , 1)");
      }
      System.out.println("使用Statement费时:" 
        + (System.currentTimeMillis() - start));
    }
  }
  public void insertUsePrepare()throws Exception
  {
    long start = System.currentTimeMillis();
    try(
      // 获取数据库连接
      Connection conn = DriverManager.getConnection(url
        , user , pass);
      // 使用Connection来创建一个PreparedStatement对象
      PreparedStatement pstmt = conn.prepareStatement(
        "insert into student_table values(null,?,1)"))

    {
      // 100次为PreparedStatement的参数设值,就可以插入100条记录
      for (int i = 0; i < 1000 ; i++ )
      {
        pstmt.setString(1 , "姓名" + i);
        pstmt.executeUpdate();
      }
      System.out.println("使用PreparedStatement费时:" 
        + (System.currentTimeMillis() - start));
    }
  }
  public static void main(String[] args) throws Exception
  {
    PreparedStatementTest pt = new PreparedStatementTest();
    pt.initParam("mysql.ini");
    pt.insertUseStatement();
    pt.insertUsePrepare();
  }
}

mysql.ini:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/select_test
user=root
pass=111

result:

使用Statement费时:71269
使用PreparedStatement费时:69226

除此之外质量上的优势,prepareStatement还有另贰个优势:
无须在SQL语句中”拼接”SQL参数,从平安的角度来看,那就从不小程序上幸免了”SQL注入”的产生。使用prepareStatement时,全数的参数都改成了”问号占位符”,也就制止了数码和代码的歪曲(那是致使注入的根本原因)

有关利用参数化预编写翻译防御SQL注入,那里要小心几点:

1. 参数化预编译之所以能防御住SQL注入,只要是基于以下2点:
    1) setString(): WEB程序接收字符串的场景
    将用户输入的参数全部强制转换为字符串,并进行适当的转义,防止了闭合的产生
    2) setInt(): WEB程序接收整型的场景
    将用户输入的非整型参数强制转换为整型,并去除潜在的"非整型注入字符",类似与PHP中的intVal()防御思路
2. 并不是说使用了参数化预编译方法执行SQL,就不会有注入的发生了,当WEB系统和DataBase系统的字符集配置不当,可能会导致宽字节注入的发生

0x3: prepareCall存款和储蓄进度对象

调用存款和储蓄进程能够利用CallableStatement,程序员通过Connection的prepareCall()方法创制CallableStatement 对象

创办存款和储蓄进度

delimiter //
create procedure add_pro(a int, b int, out sum int)
begin
set sum = a + b
end;
//

通过Connection的prepareCall()方法创立 CallableStatement 对象 时须求传入”调用存款和储蓄进程的SQL命令语句”:

cstmt = conn.prepareCall("{call add_pro(?, ?, ?)}");

存款和储蓄进度的参数既有:

1. 传入参数
java程序必须为这些参数传入值,可以通过CallableStatement的setXxx()方法为"传入参数"设置值
2. 传出参数
java程序可以通过该参数获取存储过程里的值,CallableStatement需要调用registerOutParameter()方法来注册该参数,执行结束后调用CallableStatement对象的getXxx(int index)
方法来获取指定传出参数的值

code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.sql.*;

public class CallableStatementTest
{
  private String driver;
  private String url;
  private String user;
  private String pass;
  public void initParam(String paramFile)throws Exception
  {
    // 使用Properties类来加载属性文件
    Properties props = new Properties();
    props.load(new FileInputStream(paramFile));
    driver = props.getProperty("driver");
    url = props.getProperty("url");
    user = props.getProperty("user");
    pass = props.getProperty("pass");
  }
  public void callProcedure()throws Exception
  {
    // 加载驱动
    Class.forName(driver);
    try(
      // 获取数据库连接
      Connection conn = DriverManager.getConnection(url
        , user , pass);
      // 使用Connection来创建一个CallableStatment对象
      CallableStatement cstmt = conn.prepareCall(
        "{call add_pro(?,?,?)}"))
    {
      cstmt.setInt(1, 4);
      cstmt.setInt(2, 5);
      // 注册CallableStatement的第三个参数是int类型
      cstmt.registerOutParameter(3, Types.INTEGER);
      // 执行存储过程
      cstmt.execute();
      // 获取,并输出存储过程传出参数的值。
      System.out.println("执行结果是: " + cstmt.getInt(3));
    }
  }
  public static void main(String[] args) throws Exception
  {
    CallableStatementTest ct = new CallableStatementTest();
    ct.initParam("mysql.ini");
    ct.callProcedure();
  }
}
  1. Java JDBC编制程序实践

0x1: 下载MySQL协理JDBC的驱动程序

咱俩前面说过,Java
JDBC今后主流的做法是”本地协议驱动”,为此,Java要求借助差异厂商提供的数据库驱动(本质上是3个JA陆风X8包)来和数据库举办一而再,大家那边以Mysql为例,

前往MySQL官网(http://www.mysql.com/products/connector/
)下载驱动程序,,MySQL针对差异的阳台提供了不相同的连接器,我们供给的是JDBC
Driver for MySQL (Connector/J)

图片 1

0x2:
在 MyEclips 中开创工程项目,并累加驱动JAPAJERO包

import java.sql.*;

public class ConnMySql
{
  public static void main(String[] args) throws Exception
  {
    // 1.加载驱动,使用反射的知识,现在记住这么写。
    Class.forName("com.mysql.jdbc.Driver");
    try(
      // 2.使用DriverManager获取数据库连接,
      // 其中返回的Connection就代表了Java程序和数据库的连接
      // 不同数据库的URL写法需要查驱动文档知道,用户名、密码由DBA分配
      Connection conn = DriverManager.getConnection(
        "jdbc:mysql://127.0.0.1:3306/company"
        , "root" , "111");
      // 3.使用Connection来创建一个Statment对象
      Statement stmt = conn.createStatement();
      // 4.执行SQL语句
      /*
      Statement有三种执行sql语句的方法:
      1 execute 可执行任何SQL语句。- 返回一个boolean值,
        如果执行后第一个结果是ResultSet,则返回true,否则返回false
      2 executeQuery 执行Select语句 - 返回查询到的结果集
      3 executeUpdate 用于执行DML语句。- 返回一个整数,
        代表被SQL语句影响的记录条数
      */
      ResultSet rs = stmt.executeQuery("select *"
        + " from p8_ad_user where u_id=1"))
    {
      // ResultSet有系列的getXxx(列索引 | 列名),用于获取记录指针
      // 指向行、特定列的值,不断地使用next()将记录指针下移一行,
      // 如果移动之后记录指针依然指向有效行,则next()方法返回true。
      while(rs.next())
      {
        System.out.println(rs.getInt(1) + "\t"
          + rs.getString(2) + "\t"
          + rs.getString(3) + "\t"
          + rs.getString(4));
      }
    }
  }
}

增加maven依赖

pom.xml文本中追加三个 Maven dependencies

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-jdbc-client</artifactId>
  <version>3.1.0</version>
</dependency>
<dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
  <version>2.3.3</version>
</dependency>

首先个依靠提供了vertx-jdbc-client,第三个提供了HSQL JDBC的驱动。假诺您想利用其它一个数据库,修改那么些依靠,同时你还供给修改JDBC urlJDBC driver名。

总结

那篇小说中,知道了怎么在vert.x里使用JDBC数据库,并没有过多扑朔迷离的事物。开首容许会被那么些异步的费用模型惊叹到,然而,一旦你先河使用了,你就很难再回来了。

下1回,大家将见到那些动用怎么选择mongoDB来替换HSQL。

Stay tuned, and happy coding !

全科龙婷▼升职加薪

image

关闭连接

操作完毕后,别忘了关闭SQL链接。这么些再三再四会被放入连接池并且能够被再次利用。

在这个handler的代码里,检查了statement是不是正确的实施了,假设没错,大家接下去检查表是不是带有数据,借使没有,将会选取insert艺术插入数据:

private void insert(Whisky whisky, SQLConnection connection, Handler<AsyncResult<Whisky>> next) {
  String sql = "INSERT INTO Whisky (name, origin) VALUES ?, ?";
  connection.updateWithParams(sql,
      new JsonArray().add(whisky.getName()).add(whisky.getOrigin()),
      (ar) -> {
        if (ar.failed()) {
          next.handle(Future.failedFuture(ar.cause()));
          return;
        }
        UpdateResult result = ar.result();
        // Build a new whisky instance with the generated id.
        Whisky w = new Whisky(result.getKeys().getInteger(0), whisky.getName(), whisky.getOrigin());
        next.handle(Future.succeededFuture(w));
      });
}

那几个措施运用含有INSEMuranoT(插入)statement(证明)的upateWithParams艺术,且传入了值。那么些方法幸免了SQL流入。一旦statement执行了(当数据库没有此条数据就会创建),就创设三个新的Whisky目的,自动生成ID。

初始化JDBC client

创建JDBC 客户端(client):

MyFirstVerticle类中,声美赞臣(Nutrilon)个新变量JDBCClient jdbc,并且在start措施中添加:

jdbc = JDBCClient.createShared(vertx, config(), "My-Whisky-Collection");

开创了八个JDBC client实例,使用verticle的陈设文件配置JDBC
client。那个布局文件需求提供上边的布置才能让JDBC client寻常干活:

  • url-JDBC url,例如:jdbc:hsqldb:mem:db?shutdown=true
  • _driver class-JDBC的驱动,例如:org.hsqldb.jdbcDriver

有了client,接下去需求连接数据库。连接数据库是通过运用jdbc.getConnection来贯彻的,jdbc.getConnection内需传入八个Handler<AsyncResult<SQLConnection>>参数。我们深切的垂询下这几个类型。首先,那是二个Handler,因而当结果准备好时它就会被调用。那些结果是AsyncResult<SQLConnection>的二个实例。AsyncResultvert.x提供的一个结构,使用它能够明白连接数据库的操作是打响或破产了。假如成功了,它就会提供一个结果,那里结果是一个SQLConnection的实例。

当您接到八个AsyncResult的实例时,代码平日是:

if (ar.failed()) {
  System.err.println("The operation has failed...: "
      + ar.cause().getMessage());
} else {
  // Use the result:
  result = ar.result();
 }

须求得到到SQLConnection,然后运维rest的应用。因为成为了异步的,那必要改变运转应用的章程。因而,若是将起动系列划分成多块:

startBackend(
 (connection) -> createSomeData(connection,
     (nothing) -> startWebApp(
         (http) -> completeStartup(http, fut)
     ), fut
 ), fut);
  • startBackend– 获取SQLConnection对象,然后调用下一步
  • createSomeData– 开头化数据库并插入数据。当成功后,调用下一步
  • startWebApp– 启动web应用
  • completeStartup– 最后做到运营

fut由vert.x传入,文告已经起步只怕运维进度中蒙受的标题。

startBackend方法:

private void startBackend(Handler<AsyncResult<SQLConnection>> next, Future<Void> fut) {
    jdbc.getConnection(ar -> {
      if (ar.failed()) {
        fut.fail(ar.cause());
      } else {
        next.handle(Future.succeededFuture(ar.result()));
      }
    });
  }

以此办法获得了三个SQLConnection对象,检查操作是还是不是到位。尽管成功,会调用下一步。失败了,就会告知三个不当。其他的点子服从千篇一律的形式:

  • 自作者批评上一步操作是或不是成功
  • 处管事人情逻辑
  • 调用下一步

测试

内需小小的更新下测试程序,扩充安排JDBCClient。在MyFirstVerticleTest类中,将setUp主意中开创的DeploymentOption目的修改成:

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")
        );

除了http.port,还安顿了JDBC urlJDBC使得。测试时,使用的是叁个内部存款和储蓄器数据库。在src/test/resources/my-it-config.json文件中也要做同样的修改。

{
  "http.port": ${http.port},
  "url": "jdbc:hsqldb:mem:it-test?shutdown=true",
  "driver_class": "org.hsqldb.jdbcDriver"
}

src/main/conf/my-application-conf.json文本也一致供给修改,那不是为着测试,而是为了运营那一个动用:

{
  "http.port" : 8082,
  "url": "jdbc:hsqldb:file:db/whiskies",
  "driver_class": "org.hsqldb.jdbcDriver"
}

那边那几个JDBC url和上2个文书的有点不一样,因为需求将数据仓库储存款和储蓄到硬盘中。

来得时间!

初步营造程序:

mvn clean package

尚未修改API(没有改动宣布的java文件和REST接口),测试应该是能够顺遂的运维的。

起步应用:

java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar -conf src/main/conf/my-application-conf.json

访问http://localhost:8082/assets/index.html,然后,你能够看到那么些利用使用的是数据库了。那1遍,即使重启应用,那几个数量依旧在,因为存储产品被持久化到硬盘里了。

异步?

vert.x三个很要紧的风味就是它的异步性。使用异步的API,不必要等结果回到,当有结果再次来到时,vert.x会主动通报。为了验证那一个,大家来看1个粗略的事例。

大家假若有个add主意。一般的话,会像int r = add(1, 1)那般来利用它。那是三个手拉手的API,所以你必须等到再次来到结果。异步的API会是那般:add(1, 1, r -> { /*do something with the result*/})。在那些本子中,你传入了三个Handler,当结果总结出来时才被调用。那几个主意不回来任马瑜遥西,实现如下:

public void add(int a, int b, Handler<Integer> resultHandler) {
    int r = a + b;
    resultHandler.handle(r);
}

为了制止混淆概念,异步API并不是四线程。像我们在add例子里看到的,并不曾关系二十四线程。

SQL

客户端已经准备好了,未来写SQL。从createSomeData方法起首,那么些法子也是运维顺序中的一片段:

private void createSomeData(AsyncResult<SQLConnection> result,
    Handler<AsyncResult<Void>> next, Future<Void> fut) {
    if (result.failed()) {
      fut.fail(result.cause());
    } else {
      SQLConnection connection = result.result();
      connection.execute(
          "CREATE TABLE IF NOT EXISTS Whisky (id INTEGER IDENTITY, name varchar(100), " +
          "origin varchar(100))",
          ar -> {
            if (ar.failed()) {
              fut.fail(ar.cause());
              connection.close();
              return;
            }
            connection.query("SELECT * FROM Whisky", select -> {
              if (select.failed()) {
                fut.fail(ar.cause());
                connection.close();
                return;
              }
              if (select.result().getNumRows() == 0) {
                insert(
                    new Whisky("Bowmore 15 Years Laimrig", "Scotland, Islay"),
                    connection,
                    (v) -> insert(new Whisky("Talisker 57° North", "Scotland, Island"),
                        connection,
                        (r) -> {
                          next.handle(Future.<Void>succeededFuture());
                          connection.close();
                        }));                                                    
              } else {
                next.handle(Future.<Void>succeededFuture());
                connection.close();
              }
            });
          });
    }
  }

本条主意检查SQLConnection是或不是可用,然后实施一些SQL语句。首先,如若表不设有就成立表。看看上边代码:

connection.execute(
    SQL statement,
    handler called when the statement has been executed
)

handler接收AsyncResult<Void>,例如:唯有是打招呼而已,没有实际再次来到的结果。

带有数据库(SQL)的REST

上面的措施都是运营顺序的一片段。可是,关于调用REST
API的艺术又是什么样的啊?以getAll主意为例。这些办法被web应用前端调用,并招来存款和储蓄的具有的产品:

private void getAll(RoutingContext routingContext) {
    jdbc.getConnection(ar -> {
      SQLConnection connection = ar.result();
      connection.query("SELECT * FROM Whisky", result -> {
        List<Whisky> whiskies = result.result().getRows().stream().map(Whisky::new).collect(Collectors.toList());
        routingContext.response()
            .putHeader("content-type", "application/json; charset=utf-8")
            .end(Json.encodePrettily(whiskies));
        connection.close(); // Close the connection        
      });
    });
  }

以此艺术获得了3个SQLConnection对象,然后发出1个询问。一旦取获得查询结果,它会像在此以前的不二法门一致写HTTP responsegetOnedeleteOneupdateOneaddOne办法都以同一的。注意,在response随后,要求要关闭SQL连接。

看下传入到query方法的handler提供的结果。获取了2个含有了询问结果的ResultSet。每一行都以多少个JsonObject,由此,要是你有2个数据对象使用JsonObject作为唯一的参数,那么创制这几个指标很简单。

在那篇文章中,我们将会看到什么在vert.x应用中央银行使HSQL,当然也能够应用任意JDBC,以及利用vertx-jdbc-client提供的异步的API,那篇作品的代码在github上。