SQL 客户端模版
SQL 客户端模版是一个用来方便执行SQL查询的库。
用法
要使用 SQL 客户端模版,需添加如下依赖:
-
Maven(在您的
pom.xml
文件中):
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-sql-client-templates</artifactId>
<version>4.4.0</version>
</dependency>
-
Gradle(在您的
build.gradle
文件中):
dependencies {
implementation 'io.vertx:vertx-sql-client-templates:4.4.0'
}
开始
以下是 SQL 模版最简易的使用方式。
一个 SQL 模版接收 已命名的 参数,因此,默认情况下,它会接收一个map作为参数载体,而非接收元组(tuple)作为参数。
一个SQL 模版默认情况下生成一个类似 PreparedQuery
的 RowSet<Row>
。
实际上这个模版是 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 identifier 字符串
(不受关键字约束)
您可以用反斜杠(\
)来转义 字符,例如
\{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行映射
(译者注:原文为 anemic json row mapping,即anemic mapping,指单纯的属性映射,无行为)
JSON 行映射是一个简单的模板映射,它用 toJson
将数据行映射成JSON对象。
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>
作为输入参数。
您可以提供一个自定义的映射(Mapper):
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 参数映射
(译者注:原文为 anemic json parameters mapping,即anemic mapping,指单纯的属性映射,无行为)
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的数据绑定功能做映射
您可以用Jackson的数据绑定功能来实现映射。
您需要添加 jackson-databind 依赖:
-
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}'
}
行映射是通过用键值对(key/value pair)来创建 JsonObject
实现的,然后
调用 mapTo
来将它映射为任何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
而实现的,
而后用 key/value pairs 来生成模板参数。
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 310 datatype 依赖:
-
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:processor
和 io.vertx:vertx-sql-client-templates
依赖:
以下是Maven配置示例
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-codegen</artifactId>
<version>4.4.0</version>
<classifier>processor</classifier>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-sql-client-templates</artifactId>
<version>4.4.0</version>
</dependency>
Gradle中依然可以使用此特性:
annotationProcessor "io.vertx:vertx-codegen:4.4.0:processor"
compile "io.vertx:vertx-sql-client-templates:4.4.0"
IDE都会对 annotation processors 提供支持
codegen processor
classifier 在jar中添加服务代理注解执行器的自动配置,
这是通过 META-INF/services
机制实现的。
如果需要,也可以用普通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 client)提供了支持,那么您可以映射Java枚举类型。
Java枚举类经常被映射为 string/number/自定义数据库枚举类型。
命名格式
默认的模版下,参数和数据库列之间的映射不忽略大小写。您可以在 Column
和 TemplateParameter
注解中用您喜欢的格式来重写默认名称。
您也可以在 RowMapped
和 ParametersMapped
注解中配置一个特定的大小写格式:
@DataObject
@RowMapped(formatter = SnakeCase.class)
@ParametersMapped(formatter = QualifiedCase.class)
class UserDataObject {
// ...
}
Vert.x提供了下列大小写模式:
-
CamelCase
:FirstName
-
LowerCamelCase
:firstName
- 类似驼峰命名法但是小写字母开头,这是默认方式。 -
SnakeCase
:first_name
-
KebabCase
:first-name
-
QualifiedCase
:first.name