一种在 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 中,name
是 String
类型,payload
是 Map<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.tweak
的 Arugment
接口,所以我最终得到了正在关注
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())
有没有办法将 java Map<String, Object>
绑定到 JDBI @BindBean 注释中的 varchar。
例如我有一个 class Something.class
并且我创建了一个
@SqlBatch("INSERT INTO Something (name, payload) VALUES(:name, :payload)")
。
现在在我的 java class 中,name
是 String
类型,payload
是 Map<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.tweak
的 Arugment
接口,所以我最终得到了正在关注
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())