如何使用 JOOQ 以通用方式在 PostgreSQL 中使用 JSON/JSONB 列转换任何类型的 Java 对象?
How to convert any type of Java object with JSON/JSONB column in PostgreSQL using JOOQ on a generic way?
我在问如何编写一个通用的解决方案,用于在 JSON
或 JSONB
PostgreSQL 中插入和 selecting 任何类型的 Java 对象数据库列。
此外,我必须使用(注入)我的 Jackson ObjectMapper
,因为它是一个预配置的 Spring bean。
我尝试使用自定义 ConverterProvider
来解决它,它在 select 端工作正常,但在我插入时没有调用我的转换器提供程序。我错过了什么吗?
我查看了 jOOQ 文档和一些关于 Biding
解决方案的堆栈溢出文章,但我不想确切地指定 userType
。所以我无法注册 Binding
,因为我无法声明确切的 userType
.
我的目标是:
我想消除存储库 类 中每个 JSON
和 JSONB
字段对 Java 对象字符串序列化的要求(以及注入我的 ObjectMapper
进入每个存储库)。我不想为每个 JSONB
字段编写此代码行,jOOQ 应该使用我的 JSONConverterProvider
.
.set(myJSONBTableField, JSONB.jsonb(objectMapper.writeValueAsString(myJavaObjectToStore))
我想简单地使用:
.set(myJSONBTableField, myJavaObjectToStore)
我想请求 jOOQ 在我的自定义 ObjectMapper
bean 的帮助下存储任何类型的 Java 对象,如果数据库列类型是 JSON
或 JSONB
.我的JSONBConverterProvider
是在读阶段调用的,写阶段没有调用。 JSONBConverterProvider
也有一个用于 vica 转换的转换器,但 jOOQ 不使用它。
我的 jOOQ 配置的相关部分:
@Bean
public DefaultConfigurationCustomizer ziffitJooqCustomizer(JSONConverterProvider converterProvider) {
return configuration -> configuration
.set(converterProvider)
.settings()...
我的自定义转换器提供商:
@ConditionalOnClass(DSLContext.class)
@Component
public class JSONConverterProvider implements ConverterProvider {
private final ConverterProvider delegate = new DefaultConverterProvider();
private final ObjectMapper mapper;
public JSONConverterProvider(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public <T, U> Converter<T, U> provide(Class<T> dbClass, Class<U> entityClass) {
if (dbClass == JSON.class) {
return getJSONConverter(dbClass, entityClass);
} else if (dbClass == JSONB.class) {
return getJSONBConverter(dbClass, entityClass);
} else {
// Delegate all other type pairs to jOOQ's default
return delegate.provide(dbClass, entityClass);
}
}
private <T, U> Converter<T, U> getJSONConverter(Class<T> dbClass, Class<U> entityClass) {
return Converter.ofNullable(dbClass, entityClass,
t -> {
try {
return mapper.readValue(((JSON) t).data(), entityClass);
} catch (Exception e) {
throw new DataTypeException("JSON deserialization error", e);
}
},
u -> {
try {
return (T) JSON.valueOf(mapper.writeValueAsString(u));
} catch (Exception e) {
throw new DataTypeException("JSON serialization error", e);
}
}
);
}
private <T, U> Converter<T, U> getJSONBConverter(Class<T> dbClass, Class<U> entityClass) {
return Converter.ofNullable(dbClass, entityClass,
t -> {
try {
return mapper.readValue(((JSONB) t).data(), entityClass);
} catch (Exception e) {
throw new DataTypeException("JSONB deserialization error", e);
}
},
u -> {
try {
return (T) JSONB.valueOf(mapper.writeValueAsString(u));
} catch (Exception e) {
throw new DataTypeException("JSONB serialization error", e);
}
}
);
}
}
我正在使用这样的读取查询:
dslCtx
.select(myJSONBTableField)
.from(myTable)
.where(...)
.fetchOptionalInto(myClazz);
这工作正常,请致电我的 JSONBConverterProvider
。
但我的写入查询没有。
我想像这样使用写入查询:
dslCtx
.insertInto(myTable)
.set(myJSONBTableField, myJavaObjectToStore)
.execute();
为什么 ConverterProvider
对您不起作用
ConverterProvider
SPI(以及 RecordMapperProvider
和 RecordUnmapperProvider
SPI)覆盖 [=47= 上所有基于反射的 auto-data 类型转换功能的行为] /成员匹配基础,主要影响Record::from
and Record::into
方法,以及其他类型上的相关方法。
请注意,如果您在不传递 class 文字的情况下投影您的字段,您将获得 Record1<JSONB>
记录,并且您不会期望它使用您的 JSONBConverterProvider
。因此,它不会影响任何其他类型安全的方法,例如来自 INSERT
或 UPDATE
的 set()
,也不影响 select(...).fetch()
.
什么对你有用?
相反,请将 Converter<JSONB, Object>
或更好的 (to avoid problems like this),一个 Converter<JSONB, MyMarkerInterface>
转换器附加到所有 JSONB 列,这些列应使用您的映射通用 JSON 转换逻辑。我认为您遇到的问题是您不想将任何 specific Java class 附加到 JSONB 列,因为具体class可以依赖JSONB的内容。但你不必! jOOQ 不关心 Converter<T, U>
中的 U
类型是什么。它仅用于类型安全,因此任何 MyMarkerInterface
类型都可以。
通过这种方法,您仍然可以使用 fetchOptionalInto(myClazz)
调用,它只会实现从 MyMarkerInterface
到 MyClazz
的不安全向下转换
我在问如何编写一个通用的解决方案,用于在 JSON
或 JSONB
PostgreSQL 中插入和 selecting 任何类型的 Java 对象数据库列。
此外,我必须使用(注入)我的 Jackson ObjectMapper
,因为它是一个预配置的 Spring bean。
我尝试使用自定义 ConverterProvider
来解决它,它在 select 端工作正常,但在我插入时没有调用我的转换器提供程序。我错过了什么吗?
我查看了 jOOQ 文档和一些关于 Biding
解决方案的堆栈溢出文章,但我不想确切地指定 userType
。所以我无法注册 Binding
,因为我无法声明确切的 userType
.
我的目标是:
我想消除存储库 类 中每个 JSON
和 JSONB
字段对 Java 对象字符串序列化的要求(以及注入我的 ObjectMapper
进入每个存储库)。我不想为每个 JSONB
字段编写此代码行,jOOQ 应该使用我的 JSONConverterProvider
.
.set(myJSONBTableField, JSONB.jsonb(objectMapper.writeValueAsString(myJavaObjectToStore))
我想简单地使用:
.set(myJSONBTableField, myJavaObjectToStore)
我想请求 jOOQ 在我的自定义 ObjectMapper
bean 的帮助下存储任何类型的 Java 对象,如果数据库列类型是 JSON
或 JSONB
.我的JSONBConverterProvider
是在读阶段调用的,写阶段没有调用。 JSONBConverterProvider
也有一个用于 vica 转换的转换器,但 jOOQ 不使用它。
我的 jOOQ 配置的相关部分:
@Bean
public DefaultConfigurationCustomizer ziffitJooqCustomizer(JSONConverterProvider converterProvider) {
return configuration -> configuration
.set(converterProvider)
.settings()...
我的自定义转换器提供商:
@ConditionalOnClass(DSLContext.class)
@Component
public class JSONConverterProvider implements ConverterProvider {
private final ConverterProvider delegate = new DefaultConverterProvider();
private final ObjectMapper mapper;
public JSONConverterProvider(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public <T, U> Converter<T, U> provide(Class<T> dbClass, Class<U> entityClass) {
if (dbClass == JSON.class) {
return getJSONConverter(dbClass, entityClass);
} else if (dbClass == JSONB.class) {
return getJSONBConverter(dbClass, entityClass);
} else {
// Delegate all other type pairs to jOOQ's default
return delegate.provide(dbClass, entityClass);
}
}
private <T, U> Converter<T, U> getJSONConverter(Class<T> dbClass, Class<U> entityClass) {
return Converter.ofNullable(dbClass, entityClass,
t -> {
try {
return mapper.readValue(((JSON) t).data(), entityClass);
} catch (Exception e) {
throw new DataTypeException("JSON deserialization error", e);
}
},
u -> {
try {
return (T) JSON.valueOf(mapper.writeValueAsString(u));
} catch (Exception e) {
throw new DataTypeException("JSON serialization error", e);
}
}
);
}
private <T, U> Converter<T, U> getJSONBConverter(Class<T> dbClass, Class<U> entityClass) {
return Converter.ofNullable(dbClass, entityClass,
t -> {
try {
return mapper.readValue(((JSONB) t).data(), entityClass);
} catch (Exception e) {
throw new DataTypeException("JSONB deserialization error", e);
}
},
u -> {
try {
return (T) JSONB.valueOf(mapper.writeValueAsString(u));
} catch (Exception e) {
throw new DataTypeException("JSONB serialization error", e);
}
}
);
}
}
我正在使用这样的读取查询:
dslCtx
.select(myJSONBTableField)
.from(myTable)
.where(...)
.fetchOptionalInto(myClazz);
这工作正常,请致电我的 JSONBConverterProvider
。
但我的写入查询没有。 我想像这样使用写入查询:
dslCtx
.insertInto(myTable)
.set(myJSONBTableField, myJavaObjectToStore)
.execute();
为什么 ConverterProvider
对您不起作用
ConverterProvider
SPI(以及 RecordMapperProvider
和 RecordUnmapperProvider
SPI)覆盖 [=47= 上所有基于反射的 auto-data 类型转换功能的行为] /成员匹配基础,主要影响Record::from
and Record::into
方法,以及其他类型上的相关方法。
请注意,如果您在不传递 class 文字的情况下投影您的字段,您将获得 Record1<JSONB>
记录,并且您不会期望它使用您的 JSONBConverterProvider
。因此,它不会影响任何其他类型安全的方法,例如来自 INSERT
或 UPDATE
的 set()
,也不影响 select(...).fetch()
.
什么对你有用?
相反,请将 Converter<JSONB, Object>
或更好的 (to avoid problems like this),一个 Converter<JSONB, MyMarkerInterface>
转换器附加到所有 JSONB 列,这些列应使用您的映射通用 JSON 转换逻辑。我认为您遇到的问题是您不想将任何 specific Java class 附加到 JSONB 列,因为具体class可以依赖JSONB的内容。但你不必! jOOQ 不关心 Converter<T, U>
中的 U
类型是什么。它仅用于类型安全,因此任何 MyMarkerInterface
类型都可以。
通过这种方法,您仍然可以使用 fetchOptionalInto(myClazz)
调用,它只会实现从 MyMarkerInterface
到 MyClazz