通用 R2DBC 自定义转换器

问题描述 投票:0回答:1

我正在使用带有反应式存储库和 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

任何帮助表示赞赏。

postgresql generics spring-webflux r2dbc
1个回答
0
投票

最初,我也尝试通过在 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数组的类型,只能通过嵌套对象来实现。 希望我的回答可以帮助到你和其他人。

© www.soinside.com 2019 - 2024. All rights reserved.