【问题标题】:How do I handle nullable fields using either the Mono<Connection> or the DatabaseClient provided by R2dbc in Spring?如何使用 Spring 中 R2dbc 提供的 Mono<Connection> 或 DatabaseClient 处理可为空的字段?
【发布时间】:2019-08-18 16:38:08
【问题描述】:

我不知道如何使用 spring-webflux (reactive) 在 R2dbc (java) 中构建一个有效的查询。使用 R2dbc 提供的 DatabaseClient 对象(或者,一个 Connection 对象),我似乎只能调用这两种方法之一的不同变体:bind(Object field, Object value)bindNull(Object field, Class&lt;?&gt; type)。如果我在 Java 中有一个模式和一个相应的类,并且有多个可为空的字段,那么我应该如何有效地处理这个问题?

举个例子:

public Flux<Item> saveOrUpdate(Item entity) {

   Mono<Connection> connection = this.connection; 

   Flux<? extends Result> itemFlux = connection
       .doOnError(e -> e.printStackTrace())
           .flatMapMany(connect  ->  connect.createStatement(INSERT_OR_UPDATE_ITEM)
                .bind("itemId", entity.getItemId()).returnGeneratedValues("itemid")
                .bind("auditId", entity.getTx().getId())
                .bind("itemNum", entity.getItemNum())
                .bind("itemCat", entity.getItemCat()) //nullable
                 // How would I know when to use this?
                .bindNull("sourcedQty", Integer.class) //nullable
                .bind("makeQty", entity.getMakeQty())
                .bind("nameShown", entity.getNameShown()) //nullable
                .bind("price", entity.price())
                .bind("dateCreated", entity.getDateCreated()) //nullable
                .add()
                .execute())...
   ...
}

public Mono<Item> saveOrUpdate(Item entity){

   Mono<Item> itemMono = databaseClient.execute.sql(INSERT_OR_UPDATE_ITEM)
      .bind("itemId", entity.getItemId()).returnGeneratedValues("itemid")
                .bind("auditId", entity.getTx().getId())
                .bind("itemNum", entity.getItemNum())
                .bind("itemCat", entity.getItemCat())
                .bind("sourcedQty", entity.getSourcedQty()) 
                .bind("makeQty", entity.getMakeQty())
                .bind("nameShown", entity.getNameShown())
                .bind("price", entity.price())
                .bind("dateCreated", entity.getDateCreated())
                .as(Item.class)
                .fetch()
                .one()...
   ...
}

对于我的可空字段,我当然可以用 .bindNull 替换 .bind。问题是,如果我调用绑定,则该值不能为空。如果我调用bindNull,则该值必须为null。我将如何根据我的值实际上是否为空来调用其中一个?我已经知道我可以为每个场景创建一堆方法,或者调用一些类似 retryOnError 的方法。但是,如果我想做insertOrUpdate(List&lt;Item&gt; items),这将浪费大量时间/资源。理想情况下,我想以某种方式在某处做类似于if (field == null) ? bindNull("field", field.class) : bind("field", myObj.field) 的事情。如果这显然不在桌面上,我仍然有兴趣找出一种方法来尽可能有效地实现这一点,因为我正在使用它。感谢任何反馈。

【问题讨论】:

  • 澄清您的问题:您希望保留流畅的风格,同时可能绑定null 值,而您正在询问如何实现这一目标。
  • 听起来是一种可以接受的表达方式。我的 actuql 数据集包含的空值比我在这里制作的样本多。例如,我想让与此类似的方法几乎与使用 jdbc 执行准备好的语句一样有效。我不想使用挂钩或包装器或插入不符合数据库中已有数据约定的值。所以从这个意义上说,我非常关心风格。但我对需要妥协的想法持开放态度。我是 r2dbc 和响应式的新手,所以我很可能忽略了一个简单的解决方案或一个聪明的解决方法。
  • 我也很想知道一种编写 sql 语句的方法,这样我就可以使用 .bind 并让数据库解决剩下的问题。

标签: java spring-data spring-webflux spring-data-r2dbc r2dbc


【解决方案1】:

可以使用Parameter 类来设置值或空值,如下所示:

import org.springframework.r2dbc.core.Parameter;

// rest of the code

.bind("customerId", Parameter.fromOrEmpty(o.getCustomerId(), UUID.class))

之前是SettableValue.fromOrEmpty,但未弃用。

【讨论】:

    【解决方案2】:

    这是两个问题:

    1. 如何以流畅的风格将可能为空的值绑定到Statement/DatabaseClient
    2. 如何让数据库解决剩下的问题?

    R2DBC 和 Spring Data R2DBC 通过要求将值绑定到 Statement 或绑定 null 来显式处理 null。没有接受可能为空的参数的方法。有两个原因:

    1. 您应该处理可空性以明确那里发生的情况。这是处理可空值而不是使null 处理隐式处理的好习惯。 null 的隐含性质是导致最多错误的原因。
    2. 数据库要求显式。带有占位符的参数化语句包含在两个块的执行端:SQL 语句本身和参数绑定(描述符)。参数描述符需要与占位符、类型信息(VARCHARBITINT、...)和实际值的关联。通过使用值调用bind(…),驱动程序可以派生类型信息。绑定null 值时,驱动程序需要其他类型的信息。否则,我们无法执行查询。

    话虽如此:

    1. 没有像 bindPotentiallyNull("auditId", entity.getTx().getId(), Integer.class) 这样的 API
    2. 您不能在 SQL 查询中执行任何操作,因为绑定参数信息是由辅助方法提供的。

    在谈论存储过程时,我们面临着类似的问题,因为存储过程需要有关 in/out/in-out 参数的更多详细信息。我们讨论了潜在的包装器类型,例如

    Parameters.in(@Nullable T value, Class<? super T> valueType)
    

    所以这些可以用作

    中的包装器
    bind("auditId", Parameters.in(entity.getTx().getId(), Integer.class))
    

    更多细节:

    【讨论】:

    • 感谢您的回复@mp911de。不完全是我想听到的,但仍然是很好的信息——知道绝对有用。我会继续接受这个答案。
    猜你喜欢
    • 2020-01-02
    • 1970-01-01
    • 1970-01-01
    • 2019-11-01
    • 2020-09-02
    • 2018-01-10
    • 1970-01-01
    • 1970-01-01
    • 2020-08-21
    相关资源
    最近更新 更多