我正在使用带有反应式存储库和 R2dbc 的 postgres 数据库,并且有超过 5 个 json 字段。 R2dbc 不能直接识别 jsonb 字段,因此无法将其映射到 java 对象。
所以在遵循各种文档之后 - 我开始知道一个解决方案 - 自定义转换器。
因此,我为所有领域创建了转换器。然而,后来我发现所有转换器中的代码都是相同的 - 只是映射的类不同(POJO)。 因此,我想到为转换器创建一个通用类。
但是,Spring 无法识别泛型类,而是为所有字段使用单独的转换器。
我想将来添加更多 json 字段,并且不想为每个字段创建单独的转换器类。
请帮我解决这个问题。
实体类:
@Data
@Table("person")
public class Person {
@Id
private Integer id;
private String uid;
private AddressInfo address;
...
}
为某一字段工作自定义转换器:
@ReadingConverter
@Slf4j
public class JsonToAddressInfoConverter implements Converter<Json, AddressInfo> {
@Override
public AddressInfo convert(Json json) {
try {
ObjectMapper mapper = new ObjectMapper();
AddressInfo addressInfo = mapper.readValue(json.asString(), AddressInfo.class);
return addressInfo;
} catch (IOException e) {
log.error("Problem while parsing JSON: {}", json, e);
}
return new AddressInfo();
}
}
我正在尝试创建的通用转换器 - (不起作用)
@ReadingConverter
@Slf4j
public class JsonToSimplePojoConverter<S, T> implements Converter<S, T> {
final Class<T> typeParameterClass;
public JsonToSimplePojoConverter(Class<T> typeParameterClass) {
super();
this.typeParameterClass = typeParameterClass;
}
@Override
public T convert(S json) {
T object = null;
try {
ObjectMapper mapper = new ObjectMapper();
object = mapper.readValue(((Json) json).asString(), typeParameterClass);
} catch (IOException e) {
log.error("Problem while parsing JSON: {}", json, e);
}
return object;
}
}
注册转换器的配置类:
@Configuration
public class ReactivePostgresConfig extends AbstractR2dbcConfiguration {
@Override
public ConnectionFactory connectionFactory() {
return null;
}
@Bean
@Override
public R2dbcCustomConversions r2dbcCustomConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
// converters.add(new JsonToAddressInfoConverter());
converters.add(new JsonToSimplePojoConverter< Json, AddressInfo >( AddressInfo.class));
return R2dbcCustomConversions.of(PostgresDialect.INSTANCE, converters);
}
}
我在应用程序初始化时遇到错误:
Caused by: java.lang.IllegalStateException: Couldn't resolve type arguments for class com.kredx.service.converters.JsonToSimplePojoConverter
at org.springframework.data.convert.CustomConversions$StoreConversions.getRegistrationFor(CustomConversions.java:805) ~[spring-data-commons-2.7.2.jar:2.7.2]
at org.springframework.data.convert.CustomConversions$StoreConversions.getRegistrationsFor(CustomConversions.java:787) ~[spring-data-commons-2.7.2.jar:2.7.2]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
at org.springframework.data.convert.CustomConversions.collectPotentialConverterRegistrations(CustomConversions.java:201) ~[spring-data-commons-2.7.2.jar:2.7.2]
at org.springframework.data.convert.CustomConversions.<init>(CustomConversions.java:112) ~[spring-data-commons-2.7.2.jar:2.7.2]
at org.springframework.data.r2dbc.convert.R2dbcCustomConversions.<init>(R2dbcCustomConversions.java:60) ~[spring-data-r2dbc-1.5.2.jar:1.5.2]
at org.springframework.data.r2dbc.convert.R2dbcCustomConversions.of(R2dbcCustomConversions.java:88) ~[spring-data-r2dbc-1.5.2.jar:1.5.2]
at com.kredx.service.config.ReactivePostgresConfig.r2dbcCustomConversions(ReactivePostgresConfig.java:39) ~[classes/:na]
at com.kredx.service.config.ReactivePostgresConfig$$EnhancerBySpringCGLIB$$b372d1fb.CGLIB$r2dbcCustomConversions$1(<generated>) ~[classes/:na]
at com.kredx.service.config.ReactivePostgresConfig$$EnhancerBySpringCGLIB$$b372d1fb$$FastClassBySpringCGLIB$$2d0d2d10.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.3.22.jar:5.3.22]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.3.22.jar:5.3.22]
at com.kredx.service.config.ReactivePostgresConfig$$EnhancerBySpringCGLIB$$b372d1fb.r2dbcCustomConversions(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.22.jar:5.3.22]
... 114 common frames omitted
我正在使用 Spring React + r2dbc + postgressql
任何帮助表示赞赏。
最初,我也尝试通过在 Converter 中自定义泛型类型来创建通用转换器,但没有成功(可能是因为泛型类型擦除,导致 Spring 无法读取类型信息)。 经过一番实验,我发现
GenericConverter
可以满足我们的要求。
这是代码:
@WritingConverter
public static class JsonWritingConverter implements GenericConverter {
private final Set<ConvertiblePair> convertibleTypes;
public JsonWritingConverter(Set<Class<?>> jsonTypes) {
this.convertibleTypes = new HashSet<>();
for (Class<?> targetType : jsonTypes) {
this.convertibleTypes.add(new ConvertiblePair(targetType, Json.class));
}
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.unmodifiableSet(convertibleTypes);
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return Json.of(JSON.toJSONString(source));
}
}
@ReadingConverter
public static class JsonReadingConverter implements GenericConverter {
private final Set<ConvertiblePair> convertibleTypes;
public JsonReadingConverter(Set<Class<?>> jsonTypes) {
this.convertibleTypes = new HashSet<>();
for (Class<?> targetType : jsonTypes) {
this.convertibleTypes.add(new ConvertiblePair(Json.class, targetType));
}
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.unmodifiableSet(convertibleTypes);
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Json jsonSource = (Json) source;
return JSON.parseObject(jsonSource.asString(), targetType.getType());
}
}
它可以通过重写
Source
方法,通过 Target
显式指定多个 Class<?>
和 getConvertibleTypes
类型组合。这比使用泛型可靠得多。
然后,您可以在 convert
方法中编写之前指定的组合的转换逻辑。
至于注册,和
Converter
差不多。
@Bean
public R2dbcCustomConversions r2dbcCustomConversions(ConnectionFactory connectionFactory) {
List<Object> converters = new ArrayList<>();
Reflections reflections = new Reflections("your.package.to.scan");
Set<Class<?>> jsonTypes = reflections.getTypesAnnotatedWith(StoreJson.class);
converters.add(new JsonConverter.JsonWritingConverter(jsonTypes));
converters.add(new JsonConverter.JsonReadingConverter(jsonTypes));
return R2dbcCustomConversions.of(DialectResolver.getDialect(connectionFactory), converters);
}
我使用
Reflections
扫描自定义注解,这让我可以一次性获取所有映射到自定义Json的类。
而且,由于Converter
和ConvertiblePair
没有继承关系,所以List<Object>
的泛型类型需要是Object。
使用此配置,您只需要像这样注释您的类:
@Data
@Builder
@StoreJson
public class SessionData {
private List<SessionDataItem> itemData;
}
并且它会正常工作。
但是,正如你所看到的,我不知道如何优雅地处理JSON数组的类型,只能通过嵌套对象来实现。 希望我的回答可以帮助到你和其他人。