在 Spring Data R2DBC 中处理嵌套对象的通用方法

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

在我的 Spring Boot Webflux 项目中,我使用 Spring Data R2DBC 进行反应式数据访问。该项目涉及从关系数据库中获取数据,其中一些表的列包含表示嵌套对象的 JSON 数据。

我当前的方法是获取数据,然后使用自定义读取转换器将 JSON 手动解析到我的 POJO 中。然而,当我有多个嵌套对象和不同的结构时,这个过程变得很麻烦。

这是我一直在做的事情的简化示例:

@UtilityClass
public class ConverterHelper {
    // ObjectMapper initialization

    public static <T> T readJson(Row row, String field, Class<T> clazz) {
        // implementation
    }
}

@ReadingConverter
public class CustomReadingConverter implements Converter<String, Object> {
    // implementation
}

@Configuration
public class R2dbcConfiguration extends AbstractR2dbcConfiguration {
    // implementation
}

我的问题是:是否有更通用的方法来处理这个问题,类似于 Spring Data JPA 如何自动映射嵌套对象?我不想每次遇到这种情况都编写自定义转换器。

我知道 JPA 或 Hibernate 可能会更优雅地处理这些场景,但这些不适合我的项目,因为我需要 R2DBC 提供的非阻塞 I/O。在 Spring Data R2DBC 领域是否有解决方案,或者我是否仅限于对这些复杂/嵌套对象使用自定义转换器?

任何建议或见解将不胜感激。

java spring spring-boot spring-webflux spring-data-r2dbc
2个回答
0
投票

我通过使用 json 字段以另一种方式完成此操作,然后转换为映射步骤中我需要的任何对象。

例如,我可能有一个像这样的实体:

import io.r2dbc.postgresql.codec.Json;

@Table("table_name")
@Data
@Builder
public class MyObject {
    @Id
    private Integer id;
    private Json data;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

像 DTO 类

@Data
@Builder
public class MyObjectDTO {
    private Integer id;
    private YourNestedObjectType data;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

然后是一个执行类似操作的映射类:

public MyObjectDTO to(MyObject myObj) {
    try {
        ObjectMapper objectMapper = 
        YourNestedObjectType somethingNested = objectMapper.readValue(
            myObj.getData().asString(), 
            YourNestedObjectType.class);

        return MyObjectDTO.builder()
                .id(myObj.getId())
                .data(somethingNested)
                .createdAt(myObj.getCreatedAt())
                .updatedAt(myObj.getUpdatedAt())
                .build();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

理解 YourNestedObjectType 可以是一个包含其他类的包装类,例如

public class YourNestedObjectType {
   private String field1;
   private List<OtherObject> otherObjects;
}

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.