【问题标题】:How to extract jsonb from Postgresql to Spring webflux using R2dbc如何使用 R2dbc 从 Postgresql 中提取 jsonb 到 Spring webflux
【发布时间】:2020-01-16 10:17:12
【问题描述】:

所以我真的有这个想法,因为我只编程了一小段时间,但我想构建一个反应式 Spring webflux 应用程序,将 json 端点暴露给反应前端。

当我决定在 Postgres 中使用 jsonb 格式时,问题就开始了,因为我认为我可能会从 DB 一直使用 json,直到前端层。

当我尝试使用响应式 R2dbc 驱动程序使用 jsonb 选择表时,我收到以下错误:

Caused by: java.lang.IllegalArgumentException: 3802 is not a valid object id

我在 postgres 中有一个如下所示的表:

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 个问题,如何使用响应式驱动程序选择包含 jsonb 的表,我是否在浪费时间尝试这样做,将 json 提取为文本并从中创建一个正常的 POJO 足够了吗?

感谢您的宝贵时间!

【问题讨论】:

    标签: java postgresql spring-webflux jsonb r2dbc


    【解决方案1】:

    更新:请升级到 R2DBC Postgres 0.8.0.RC1。

    驱动程序添加了recently 对 JSON 和 JSONB 类型的支持。您可以使用 Stringbyte[]io.r2dbc.postgresql.codec.Json 类型的 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($1)")
            .bind("$1", Json.of("{\"hello\": \"world\"}"))
            .execute()
    
    // Write JSON as String using ::JSON casting
    connection.createStatement("INSERT INTO my_table (my_json) VALUES($1::JSON)")
            .bind("$1", "{\"hello\": \"world\"}")
            .execute()
    

    请注意,当您想为 SELECTINSERTUPDATE 绑定 JSON 值时,您必须使用驱动程序 Json 类型或将带有 $1::JSON 的绑定值强制转换为 JSON。

    您还可以利用驱动程序的CodecRegistrar 提供自己的JsonCodec 实现,例如想要使用 GSON 或 Jackson 在驱动程序级别上映射序列化/反序列化值。

    参考资料:

    【讨论】:

    • 非常感谢!当然,在我挣扎了这么多之后的 2 周后,我们会添加支持哈哈。
    【解决方案2】:

    更新:

    截至撰写本文时(2019 年 9 月 15 日),他们不支持 JSON,但现在他们支持 0.8.0 及更高版本的 JSON

    旧答案:

    很抱歉,我认为你是,正如你所说的,浪费你的时间

    在查看 R2DBC 驱动程序的 github 时,您可以在他们的表格中看到他们支持的内容,该 json 目前是不支持

    【讨论】:

      【解决方案3】:

      正如@mp911de 所解释的,R2DBC 现在包含一个用于 Json 的编解码器。但是将您的实体属性定义为Stringbyte[] 不是很舒服,并且类型io.r2dbc.postgresql.codec.Json 不是很便携。此外,如果您想在 REST API 中使用它,您需要定义一个自定义序列化器/反序列化器,如参考资料中所述。

      一个更好的选择是定义一个自定义转换器 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("");
          }
      }
      }
      

      参考资料:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-08-24
        • 1970-01-01
        • 2021-08-29
        • 1970-01-01
        • 2017-06-23
        • 2021-08-03
        • 1970-01-01
        • 2020-04-30
        相关资源
        最近更新 更多