如何使用 R2dbc 从 Postgresql 中提取 jsonb 到 Spring webflux
How to extract jsonb from Postgresql to Spring webflux using R2dbc
所以我确实有这个想法,因为我只编程了一小会儿,但我想构建一个反应式 Spring webflux 应用程序,将 json 端点暴露给一个反应前端。
当我决定在 Postgres 中使用 jsonb 格式时,问题就开始了,因为我认为我可能会从数据库一直使用 json 直到前端层。
当我尝试使用反应式 R2dbc 驱动程序 SELECT table 和 jsonb 时,出现以下错误:
Caused by: java.lang.IllegalArgumentException: 3802 is not a valid object id
我在 postgres 中有一个 table,看起来像这样:
Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+------------------------------
id | integer | | not null | generated always as identity
details | jsonb | | |
Indexes:
"snacks_new_pkey" PRIMARY KEY, btree (id)
因此,如果我将其作为文本提取到 Spring webflux,它可以正常工作,因为它不再是 json。
"SELECT id, details->>'name' as NAME, details->>'price' AS PRICE, details->>'quantity' AS QUANTITY FROM snacks_new"
我看过一些关于如何使用较旧的阻塞驱动程序将 jsonb 转换为 json 对象的示例,但我无法使用较新的非阻塞驱动程序那些,我无法以任何方式访问它们。
所以我真的有 2 个问题,我如何 SELECT 包含 jsonb 的 table 使用反应式驱动程序,我是否在浪费时间尝试这样做,是将 json 提取为文本并从中创建一个普通的 POJO 就够了吗?
感谢您的宝贵时间!
更新:
截至撰写本文时(2019 年 9 月 15 日)他们不支持 JSON,但现在他们支持 JSON 版本 0.8.0 及更高版本
旧答案:
很抱歉,我认为你是,正如你所说的那样,在浪费你的时间。
在查看 R2DBC 驱动程序的 github 时,您可以在他们的 table 中看到他们支持的内容,json 是截至目前,不支持.
更新:请升级到 R2DBC Postgres 0.8.0.RC1。
驱动程序添加了 recently 对 JSON 和 JSONB 类型的支持。您可以将 JSON 作为 String
、byte[]
或 io.r2dbc.postgresql.codec.Json
类型使用:
// Read as Json
connection.createStatement("SELECT my_json FROM my_table")
.execute()
.flatMap(it -> it.map((row, rowMetadata) -> row.get("my_json", Json.class)))
.map(Json::asString)
// Read as String
connection.createStatement("SELECT my_json FROM my_table")
.execute()
.flatMap(it -> it.map((row, rowMetadata) -> row.get("my_json", String.class)))
// Write JSON
connection.createStatement("INSERT INTO my_table (my_json) VALUES()")
.bind("", Json.of("{\"hello\": \"world\"}"))
.execute()
// Write JSON as String using ::JSON casting
connection.createStatement("INSERT INTO my_table (my_json) VALUES(::JSON)")
.bind("", "{\"hello\": \"world\"}")
.execute()
请注意,当您想要为 SELECT
、INSERT
或 UPDATE
绑定 JSON 值时,您必须使用驱动程序 Json
将 ::JSON
的绑定值键入或转换为 JSON.
您还可以利用驱动程序的 CodecRegistrar
提供您自己的 JsonCodec
实现,例如,想使用 GSON 或 Jackson 在驱动级别上映射 serialize/deserialize 值。
参考文献:
如 @mp911de 所述,R2DBC 现在包括 Json 的编解码器。但是将您的实体属性定义为 String
、byte[]
不是很舒服,并且类型 io.r2dbc.postgresql.codec.Json
不是很便携。此外,如果您想在 REST API 中使用它,则需要定义自定义 serializer/deserializer,如参考资料中所述。
一个更好的选择是定义一个自定义转换器 io.r2dbc.postgresql.codec.Json
from/to JsonNode
并将此后一种类型用于您的属性:
@Configuration
public class ReactivePostgresConfig {
private final ObjectMapper objectMapper;
public ReactivePostgresConfig(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Bean
public R2dbcCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new JsonToJsonNodeConverter(objectMapper));
converters.add(new JsonNodeToJsonConverter(objectMapper));
return R2dbcCustomConversions.of(PostgresDialect.INSTANCE, converters);
}
@ReadingConverter
static class JsonToJsonNodeConverter implements Converter<Json, JsonNode> {
private final ObjectMapper objectMapper;
public JsonToJsonNodeConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public JsonNode convert(Json json) {
try {
return objectMapper.readTree(json.asString());
} catch (IOException e) {
LOG.error("Problem while parsing JSON: {}", json, e);
}
return objectMapper.createObjectNode();
}
}
@WritingConverter
static class JsonNodeToJsonConverter implements Converter<JsonNode, Json> {
private final ObjectMapper objectMapper;
public JsonNodeToJsonConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Json convert(JsonNode source) {
try {
return Json.of(objectMapper.writeValueAsString(source));
} catch (JsonProcessingException e) {
LOG.error("Error occurred while serializing map to JSON: {}", source, e);
}
return Json.of("");
}
}
}
参考文献:
- Mapping to JSON Fields with Spring Data R2DBC and Reactive Postgres Driver. Check the new way of registering converters in this answer.
- How to define custom serializers for io.r2dbc.postgresql.codec.Json
所以我确实有这个想法,因为我只编程了一小会儿,但我想构建一个反应式 Spring webflux 应用程序,将 json 端点暴露给一个反应前端。
当我决定在 Postgres 中使用 jsonb 格式时,问题就开始了,因为我认为我可能会从数据库一直使用 json 直到前端层。
当我尝试使用反应式 R2dbc 驱动程序 SELECT table 和 jsonb 时,出现以下错误:
Caused by: java.lang.IllegalArgumentException: 3802 is not a valid object id
我在 postgres 中有一个 table,看起来像这样:
Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+------------------------------
id | integer | | not null | generated always as identity
details | jsonb | | |
Indexes:
"snacks_new_pkey" PRIMARY KEY, btree (id)
因此,如果我将其作为文本提取到 Spring webflux,它可以正常工作,因为它不再是 json。
"SELECT id, details->>'name' as NAME, details->>'price' AS PRICE, details->>'quantity' AS QUANTITY FROM snacks_new"
我看过一些关于如何使用较旧的阻塞驱动程序将 jsonb 转换为 json 对象的示例,但我无法使用较新的非阻塞驱动程序那些,我无法以任何方式访问它们。
所以我真的有 2 个问题,我如何 SELECT 包含 jsonb 的 table 使用反应式驱动程序,我是否在浪费时间尝试这样做,是将 json 提取为文本并从中创建一个普通的 POJO 就够了吗?
感谢您的宝贵时间!
更新:
截至撰写本文时(2019 年 9 月 15 日)他们不支持 JSON,但现在他们支持 JSON 版本 0.8.0 及更高版本
旧答案:
很抱歉,我认为你是,正如你所说的那样,在浪费你的时间。
在查看 R2DBC 驱动程序的 github 时,您可以在他们的 table 中看到他们支持的内容,json 是截至目前,不支持.
更新:请升级到 R2DBC Postgres 0.8.0.RC1。
驱动程序添加了 recently 对 JSON 和 JSONB 类型的支持。您可以将 JSON 作为 String
、byte[]
或 io.r2dbc.postgresql.codec.Json
类型使用:
// Read as Json
connection.createStatement("SELECT my_json FROM my_table")
.execute()
.flatMap(it -> it.map((row, rowMetadata) -> row.get("my_json", Json.class)))
.map(Json::asString)
// Read as String
connection.createStatement("SELECT my_json FROM my_table")
.execute()
.flatMap(it -> it.map((row, rowMetadata) -> row.get("my_json", String.class)))
// Write JSON
connection.createStatement("INSERT INTO my_table (my_json) VALUES()")
.bind("", Json.of("{\"hello\": \"world\"}"))
.execute()
// Write JSON as String using ::JSON casting
connection.createStatement("INSERT INTO my_table (my_json) VALUES(::JSON)")
.bind("", "{\"hello\": \"world\"}")
.execute()
请注意,当您想要为 SELECT
、INSERT
或 UPDATE
绑定 JSON 值时,您必须使用驱动程序 Json
将 ::JSON
的绑定值键入或转换为 JSON.
您还可以利用驱动程序的 CodecRegistrar
提供您自己的 JsonCodec
实现,例如,想使用 GSON 或 Jackson 在驱动级别上映射 serialize/deserialize 值。
参考文献:
如 @mp911de 所述,R2DBC 现在包括 Json 的编解码器。但是将您的实体属性定义为 String
、byte[]
不是很舒服,并且类型 io.r2dbc.postgresql.codec.Json
不是很便携。此外,如果您想在 REST API 中使用它,则需要定义自定义 serializer/deserializer,如参考资料中所述。
一个更好的选择是定义一个自定义转换器 io.r2dbc.postgresql.codec.Json
from/to JsonNode
并将此后一种类型用于您的属性:
@Configuration
public class ReactivePostgresConfig {
private final ObjectMapper objectMapper;
public ReactivePostgresConfig(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Bean
public R2dbcCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new JsonToJsonNodeConverter(objectMapper));
converters.add(new JsonNodeToJsonConverter(objectMapper));
return R2dbcCustomConversions.of(PostgresDialect.INSTANCE, converters);
}
@ReadingConverter
static class JsonToJsonNodeConverter implements Converter<Json, JsonNode> {
private final ObjectMapper objectMapper;
public JsonToJsonNodeConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public JsonNode convert(Json json) {
try {
return objectMapper.readTree(json.asString());
} catch (IOException e) {
LOG.error("Problem while parsing JSON: {}", json, e);
}
return objectMapper.createObjectNode();
}
}
@WritingConverter
static class JsonNodeToJsonConverter implements Converter<JsonNode, Json> {
private final ObjectMapper objectMapper;
public JsonNodeToJsonConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Json convert(JsonNode source) {
try {
return Json.of(objectMapper.writeValueAsString(source));
} catch (JsonProcessingException e) {
LOG.error("Error occurred while serializing map to JSON: {}", source, e);
}
return Json.of("");
}
}
}
参考文献:
- Mapping to JSON Fields with Spring Data R2DBC and Reactive Postgres Driver. Check the new way of registering converters in this answer.
- How to define custom serializers for io.r2dbc.postgresql.codec.Json