一种在 JDBI INSERT 语句中将 Java Map<String, Object> 绑定到 sql varchar 的方法

A way to bind Java Map<String, Object> to sql varchar in JDBI INSERT statement

有没有办法将 java Map<String, Object> 绑定到 JDBI @BindBean 注释中的 varchar。

例如我有一个 class Something.class 并且我创建了一个

@SqlBatch("INSERT INTO Something (name, payload) VALUES(:name, :payload)")

现在在我的 java class 中,nameString 类型,payloadMap<String, Object> 类型,我想要DB table 类型是 varchar(...)。现在我希望 Map 对象作为 JSON 对象插入到列中,如果不创建我自己的 http://jdbi.org/sql_object_api_argument_binding/ 中定义的复杂活页夹,是否可以以某种方式实现?除了让我的有效载荷在 java 中成为 String 类型之外。

解决了我创建 this post 中建议的 ArgumentFactory 活页夹的问题。

所以我需要的是创建一个 class,它只包含一个 Map<String, Object> 类型的字段,实现了 org.skife.jdbi.v2.tweakArugment 接口,所以我最终得到了正在关注

public class NotificationPayloadArgument implements Argument {
  private NotificationPayload payload;

  NotificationPayloadArgument(NotificationPayload payload) {
      this.payload = payload;
  }
    
  @Override
  public void apply(int i, PreparedStatement preparedStatement, StatementContext statementContext)
    throws SQLException {
      preparedStatement.setString(i, toString());
  }

  @Override
  public String toString() {
      return new JSONObject(payload).toString();
  } 

}

为了完成这项工作,我当然需要实现一个工厂 class,它使用我新创建的类型实现 org.skife.jdbi.v2.tweak.ArgumentFactory<T> 接口,所以工厂最终是这样的:

public class NotificationPayloadFactory implements ArgumentFactory<NotificationPayload> {

    @Override
    public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
        return value instanceof NotificationPayload;
    }

    @Override
    public Argument build(Class<?> expectedType, NotificationPayload value, StatementContext ctx) {
        return value;
    }

}

当然最后也如Does JDBI accept UUID parameters?中所述,我必须注册我的工厂:

jdbi.registerArgumentFactory(new NotificationPayloadFactory());

我试着用一个工厂来做这个 Map<String, Object> 但不能让它工作,而且有 NPE 的风险。

编辑

我在 NotificationPayload 中覆盖 toString() 的原因是因为我需要 json 格式的有效负载,而在某些地方我需要 String 对象。但否则它可以被删除,然后只需在 preparedStatement.setString() 中使用 new JSONObject(payload).toString(),其中 toString() 被调用。

    public class MapArgument implements Argument {

    private Map<String, Object> payload;

    private ObjectMapper objectMapper;

    public MapArgument(ObjectMapper objectMapper, Map<String, Object> payload) {
        this.objectMapper = objectMapper;
        this.payload = payload;
    }

    @Override
    public void apply(int i, PreparedStatement statement, StatementContext statementContext) throws SQLException {
        try {
            statement.setString(i, objectMapper.writeValueAsString(payload));
        } catch (JsonProcessingException e) {
            log.info("Failed to serialize payload ", e);
        }
    }
}

    public class MapArgumentFactory implements ArgumentFactory<Map<String, Object>> {

    private ObjectMapper mapper;

    public MapArgumentFactory(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public boolean accepts(Class<?> type, Object value, StatementContext statementContext) {
        return value instanceof Map;
    }

    @Override
    public Argument build(Class<?> type, Map<String, Object> map, StatementContext statementContext) {
        return new MapArgument(mapper, map);
    }
}

在kotlin-way中使用JDBI插入数据class作为JSONB;只需为该数据类型创建一个 ArgumentFactory 并向 JDBI 注册该工厂。这就像需要这 3 行一样简单 -

internal class CvMetadataArgumentFactory : AbstractArgumentFactory<CvMetadata?>(Types.OTHER) {
override fun build(value: CvMetadata?, config: ConfigRegistry): Argument {
    return Argument { position, statement, _ -> statement.setString(position, value?.toString()?:"") }
}

}

稍后注册这个工厂-

jdbi.registerArgument(CvMetadataArgumentFactory())