SQL 客户端模板

SQL 客户端模板是一个小型库,旨在简化 SQL 查询的执行。

用法

要使用 SQL 客户端模板,请将以下依赖项添加到构建描述符的 dependencies 部分

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-sql-client-templates</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

dependencies {
  implementation 'io.vertx:vertx-sql-client-templates:5.0.1'
}

快速入门

以下是使用 SQL 模板的最简单方法。

SQL 模板使用命名参数,因此(默认情况下)接受映射作为参数源,而不是元组。

SQL 模板(默认情况下)生成 RowSet<Row>,类似于客户端的 PreparedQuery。实际上,模板是 PreparedQuery 的一个轻量级包装器。

Map<String, Object> parameters = Collections.singletonMap("id", 1);

SqlTemplate
  .forQuery(client, "SELECT * FROM users WHERE id=#{id}")
  .execute(parameters)
  .onSuccess(users -> {
    users.forEach(row -> {
      System.out.println(row.getString("first_name") + " " + row.getString("last_name"));
    });
  });

当您需要执行插入或更新操作且不关心结果时,可以使用 SqlTemplate.forUpdate 替代

Map<String, Object> parameters = new HashMap<>();
parameters.put("id", 1);
parameters.put("firstName", "Dale");
parameters.put("lastName", "Cooper");

SqlTemplate
  .forUpdate(client, "INSERT INTO users VALUES (#{id},#{firstName},#{lastName})")
  .execute(parameters)
  .onSuccess(v -> {
    System.out.println("Successful update");
  });

模板语法

模板语法使用 #{XXX},其中 XXX 是一个有效的 Java 标识符字符串(无关键字限制)。

您可以使用反斜杠字符 \ 来转义任何 字符,即 \{foo} 将被解释为字符串 #{foo},而不是一个 foo 参数。

行映射

默认情况下,模板生成 Row 作为结果类型。

您可以提供自定义的 RowMapper 来实现行级别映射

RowMapper<User> ROW_USER_MAPPER = row -> {
  User user = new User();
  user.id = row.getInteger("id");
  user.firstName = row.getString("firstName");
  user.lastName = row.getString("lastName");
  return user;
};

以实现行级别映射

SqlTemplate
  .forQuery(client, "SELECT * FROM users WHERE id=#{id}")
  .mapTo(ROW_USER_MAPPER)
  .execute(Collections.singletonMap("id", 1))
  .onSuccess(users -> {
    users.forEach(user -> {
      System.out.println(user.firstName + " " + user.lastName);
    });
  });

贫血 JSON 行映射

贫血 JSON 行映射是模板行字段与 JSON 对象之间使用 toJson 进行的简单映射

SqlTemplate
  .forQuery(client, "SELECT * FROM users WHERE id=#{id}")
  .mapTo(Row::toJson)
  .execute(Collections.singletonMap("id", 1))
  .onSuccess(users -> {
    users.forEach(user -> {
      System.out.println(user.encode());
    });
  });

参数映射

模板默认接受 Map<String, Object> 作为输入。

您可以提供自定义映射器

TupleMapper<User> PARAMETERS_USER_MAPPER = TupleMapper.mapper(user -> {
  Map<String, Object> parameters = new HashMap<>();
  parameters.put("id", user.id);
  parameters.put("firstName", user.firstName);
  parameters.put("lastName", user.lastName);
  return parameters;
});

以实现参数映射

User user = new User();
user.id = 1;
user.firstName = "Dale";
user.firstName = "Cooper";

SqlTemplate
  .forUpdate(client, "INSERT INTO users VALUES (#{id},#{firstName},#{lastName})")
  .mapFrom(PARAMETERS_USER_MAPPER)
  .execute(user)
  .onSuccess(res -> {
    System.out.println("User inserted");
  });

您还可以轻松地执行批量操作

SqlTemplate
  .forUpdate(client, "INSERT INTO users VALUES (#{id},#{firstName},#{lastName})")
  .mapFrom(PARAMETERS_USER_MAPPER)
  .executeBatch(users)
  .onSuccess(res -> {
    System.out.println("Users inserted");
  });

贫血 JSON 参数映射

贫血 JSON 参数映射是模板参数与 JSON 对象之间进行的简单映射

JsonObject user = new JsonObject();
user.put("id", 1);
user.put("firstName", "Dale");
user.put("lastName", "Cooper");

SqlTemplate
  .forUpdate(client, "INSERT INTO users VALUES (#{id},#{firstName},#{lastName})")
  .mapFrom(TupleMapper.jsonObject())
  .execute(user)
  .onSuccess(res -> {
    System.out.println("User inserted");
  });

使用 Jackson databind 进行映射

您可以使用 Jackson databind 的功能进行映射。

您需要将 Jackson databind 依赖项添加到构建描述符的 dependencies 部分

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>${jackson.version}</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

dependencies {
  compile 'com.fasterxml.jackson.core:jackson-databind:${jackson.version}'
}

行映射是通过使用行键/值对创建 JsonObject,然后调用 mapTo,以 Jackson databind 将其映射到任何 Java 类来实现的。

SqlTemplate
  .forQuery(client, "SELECT * FROM users WHERE id=#{id}")
  .mapTo(User.class)
  .execute(Collections.singletonMap("id", 1))
  .onSuccess(users -> {
    users.forEach(user -> {
      System.out.println(user.firstName + " " + user.lastName);
    });
  });

同样,参数映射是通过使用 JsonObject.mapFrom 将对象映射到 JsonObject,然后使用键/值对生成模板参数来实现的。

User u = new User();
u.id = 1;

SqlTemplate
  .forUpdate(client, "INSERT INTO users VALUES (#{id},#{firstName},#{lastName})")
  .mapFrom(User.class)
  .execute(u)
  .onSuccess(res -> {
    System.out.println("User inserted");
  });

Java 日期/时间 API 映射

您可以使用 jackson-modules-java8 Jackson 扩展来映射 java.time 类型。

您需要将 Jackson JSR 310 数据类型依赖项添加到构建描述符的 dependencies 部分

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>${jackson.version}</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

dependencies {
  compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jackson.version}'
}

然后您需要将时间模块注册到 Jackson ObjectMapper

ObjectMapper mapper = io.vertx.core.json.jackson.DatabindCodec.mapper();

mapper.registerModule(new JavaTimeModule());

您现在可以使用 java.time 类型,例如 LocalDateTime

public class LocalDateTimePojo {

  public LocalDateTime localDateTime;

}

使用 Vert.x 数据对象进行映射

SQL 客户端模板组件可以为 Vert.x 数据对象生成映射函数。

Vert.x 数据对象是一个使用 @DataObject 注解的简单 Java bean 类。

@DataObject
class UserDataObject {

  private long id;
  private String firstName;
  private String lastName;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

代码生成

任何用 @RowMapped@ParametersMapped 注解的数据对象将触发相应映射器类的生成。

codegen 注解处理器在编译时生成这些类。这是 Java 编译器的一个特性,因此无需额外步骤,只需正确配置您的构建即可。

只需将 io.vertx:vertx-codegen:processorio.vertx:vertx-sql-client-templates 依赖项添加到您的构建中。

以下是 Maven 的配置示例

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-codegen</artifactId>
  <version>5.0.1</version>
  <classifier>processor</classifier>
</dependency>
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-sql-client-templates</artifactId>
  <version>5.0.1</version>
</dependency>

此功能也可用于 Gradle

annotationProcessor "io.vertx:vertx-codegen:5.0.1:processor"
compile "io.vertx:vertx-sql-client-templates:5.0.1"

IDE 通常支持注解处理器。

codegen processor 分类器通过 META-INF/services 插件机制,为 jar 添加服务代理注解处理器的自动配置。

如果您愿意,也可以将其与常规 jar 一起使用,但那时您需要显式声明注解处理器,例如在 Maven 中

<plugin>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <annotationProcessors>
      <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
    </annotationProcessors>
  </configuration>
</plugin>

行映射

您可以通过使用 @RowMapped 注解数据对象来生成行映射器。

@DataObject
@RowMapped
class UserDataObject {

  private long id;
  private String firstName;
  private String lastName;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

默认情况下,每个列名都与数据对象属性绑定,例如 userName 属性绑定到 userName 列。

您可以使用 @Column 注解来自定义名称。

@DataObject
@RowMapped
class UserDataObject {

  private long id;
  @Column(name = "first_name")
  private String firstName;
  @Column(name = "last_name")
  private String lastName;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

您可以注解字段、getter 或 setter。

生成的映射器可用于执行行映射,如行映射章节中所述。

SqlTemplate
  .forQuery(client, "SELECT * FROM users WHERE id=#{id}")
  .mapTo(UserDataObjectRowMapper.INSTANCE)
  .execute(Collections.singletonMap("id", 1))
  .onSuccess(users -> {
    users.forEach(user -> {
      System.out.println(user.getFirstName() + " " + user.getLastName());
    });
  });

参数映射

您可以通过使用 @ParametersMapped 注解数据对象来生成参数映射器。

@DataObject
@ParametersMapped
class UserDataObject {

  private long id;
  private String firstName;
  private String lastName;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

默认情况下,每个参数都与数据对象属性绑定,例如 userName 属性绑定到 userName 参数。

您可以使用 @TemplateParameter 注解来自定义名称。

@DataObject
@ParametersMapped
class UserDataObject {

  private long id;
  @TemplateParameter(name = "first_name")
  private String firstName;
  @TemplateParameter(name = "last_name")
  private String lastName;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

您可以注解字段、getter 或 setter。

生成的映射器可用于执行参数映射,如参数映射章节中所述。

UserDataObject user = new UserDataObject().setId(1);

SqlTemplate
  .forQuery(client, "SELECT * FROM users WHERE id=#{id}")
  .mapFrom(UserDataObjectParamMapper.INSTANCE)
  .execute(user)
  .onSuccess(users -> {
    users.forEach(row -> {
      System.out.println(row.getString("firstName") + " " + row.getString("lastName"));
    });
  });

Java 枚举类型映射

当客户端支持时(例如 Reactive PostgreSQL 客户端),您可以映射 Java 枚举类型。

通常,Java 枚举类型被映射为字符串/数字,也可能映射为自定义数据库枚举类型。

命名格式

默认模板对参数和列使用相同的命名大小写。您可以在 ColumnTemplateParameter 注解中覆盖默认名称,并使用您喜欢的格式。

您还可以在 RowMappedParametersMapped 注解中配置映射器的特定格式大小写

@DataObject
@RowMapped(formatter = SnakeCase.class)
@ParametersMapped(formatter = QualifiedCase.class)
class UserDataObject {
  // ...
}

可以使用以下命名大小写

  • CamelCase (大驼峰命名法) : FirstName

  • LowerCamelCase (小驼峰命名法) : firstName - 类似于大驼峰命名法但以小写字母开头,这是默认情况

  • SnakeCase (蛇形命名法) : first_name

  • KebabCase (烤串命名法) : first-name

  • QualifiedCase (限定命名法) : first.name