SQL Client Templates翻译

925 阅读6分钟

SQL Client Templates

SQL Client Templates 是一个被设计为方便执行SQL查询的小型库

使用方法

使用SQL Client Templates前请将以下依赖加入你的项目中

  • Maven (在你的pom.xml中):
<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-sql-client-templates</artifactId>
 <version>4.0.0.Beta3</version>
</dependency>
  • Gradle (在build.gradle 文件中):
dependencies {
 implementation 'io.vertx:vertx-sql-client-templates:4.0.0.Beta3'
}

开始

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

一个SQL模板使用带名字的参数,因此它在默认情况下使用map而不是tuple作为参数源

一个SQL模板默认情况下会生成一个RowSet<Row>就像client中的PreparedQuery方法做的那样。事实上模板只是PreparedQuery的一层薄封装

(译者:类似于mybatis)

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

当你需要执行insert或者update操作而且你并不关心结果时,你可以使用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`参数。

(译者:就是直接获取#{foo},而不是会被替换为?再替换为实际值)

行映射

一般情况下模板会产生Row作为结果类型

你也可以提供一个函数实现行级别的映射

Function<Row, 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);
    });
  });

参数映射

模板可以使用Map<String,Object>作为默认输入

你可以提供这样一个函数

Function<User, Map<String, Object>> PARAMETERS_USER_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");
  });

使用Jackson databind进行映射

你可以使用Jackson databind功能进行映射

你需要把jackson databind依赖加入你的项目

  • Maven (in your pom.xml):
<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-databind</artifactId>
 <version>${jackson.version}</version>
</dependency>
  • Gradle (in your build.gradle file):
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 Date/Time 映射的API

你可以使用jackson-modules-java8的Jackson拓展来映射java.time类型

你需要将Jackson JSR 31 datatype依赖加入你的依赖

  • Maven (in your pom.xml):
<dependency>
 <groupId>com.fasterxml.jackson.datatype</groupId>
 <artifactId>jackson-datatype-jsr310</artifactId>
 <version>${jackson.version}</version>
</dependency>
  • Gradle (in your build.gradle file):
dependencies {
 compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jackson.version}'
}

然后你可以将time模块注册到Jackson ObjectMapper

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

mapper.registerModule(new JavaTimeModule());

你现在可以使用LocalDateTime这样的java.time类型了

public class LocalDateTimePojo {

 public LocalDateTime localDateTime;

}

使用Vert.x data objects进行映射

SQL Client Template组件可以为Vert.x data objects生成映射函数

一个Vert.x data objects就是一个被标注@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或者@ParamtersMapped注解的data objectd都会触发生成相应的映射类

codegen注解处理器在编译期生成这些类。这是Java编译器的一个特性,因此不需要额外的步骤,只需要正确配置

只要添加io.vertx:vertx-codegen:processorio.vertx:vertx-sql-client-templates到你的项目中

下面是使用Maven配置的例子

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

这个特性也可以在Gradle中使用

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

IDE一般会提供这些注解处理器的支持

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

如果你想,你也可以常规的jar中使用它,但你需要显式声明注释处理器,例如在Maven:

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

行映射

你可以通过给你的data object 添加 @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;
  }
}

You can use custom names thanks to the @Column annotation.

默认情况下,每个列名都绑定在数据对象属性之后,例如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注解你的data object来生成参数映射

@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枚举类型映射

当你使用的SQL客户端支持的时候,你可以使用java枚举类的映射(比如,Reactive PostgreSQL client)

通常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