【问题标题】:Spring Data R2DBC custom conversions does not work with json arraySpring Data R2DBC 自定义转换不适用于 json 数组
【发布时间】:2021-05-06 11:46:02
【问题描述】:

我正在尝试获取 Spring Data R2DBC 的自定义转换来处理 json_array 字段。

我有一个由下面的 SQL 创建的 MySQL 表。

create table prefecture
(
    id         bigint unsigned auto_increment,
    region_ids json       not null,
    created_at datetime   not null default current_timestamp,
    updated_at datetime   not null default current_timestamp on update current_timestamp,
    constraint prefecture_pk
        primary key (id)
);

--- initial data
INSERT INTO prefecture
VALUES (null, "1", "TEST", json_array(1), now(), now());

然后,我定义了一个与该表对应的实体

data class Prefecture(
    @Id
    val id: Int? = null,
    val regionIds: RegionIds,
)

data class RegionIds(
    val list: List<Int>
)

根据这个doc,我发现需要注册一个自定义转换器,对应一个源类型和一个目标类型,所以我实现了一个转换器,并像下面这样注册。

@Configuration
class R2dbcConfiguration {
    @Bean
    fun customConversions(): R2dbcCustomConversions {
        val converters: MutableList<Converter<*, *>?> = ArrayList()
        converters.add(RegionIdsReadConverter())
        return R2dbcCustomConversions(converters)
    }
}

# guessing Row is not appropriate type to source?
@ReadingConverter
class RegionIdsReadConverter : Converter<Row, RegionIds> {
    override fun convert(source: Row): RegionIds {
        val regionIdsString = source.get("region_ids", String::class.java)
        val mapper = ObjectMapper()
        return RegionIds(mapper.readValue(regionIdsString, object : TypeReference<List<Int>>() {}))
    }
}

然后,我尝试通过 ReactiveCrudRepository 获取数据,但出现错误:

Could not read property private final jp.foo.bar.models.test.RegionIds jp.foo.bar.models.test.Prefecture.regionIds from column region_ids!
at org.springframework.data.r2dbc.convert.MappingR2dbcConverter.readFrom(MappingR2dbcConverter.java:177) ~[spring-data-r2dbc-1.2.8.jar:1.2.8]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Handler jp.foo.bar.controllers.PrefectureController#getAllPrefectures() [DispatcherHandler]

另外,我尝试使用 r2dbc-mysql 的Custom Codecs,但是由于the fix related to this feature 还没有发布,它并没有失败。

我正在使用

  • MySQL 5.7
  • spring-boot-starter-data-r2dbc
  • r2dbc-mysql 0.8.2.RELEASE

我是否遗漏或误解了什么?

【问题讨论】:

  • 我也遇到了同样的错误

标签: mysql spring-data-r2dbc r2dbc-mysql


【解决方案1】:

最后,我找到了解决这个问题的方法。

首先,我删除了RegionIdsReadConverter(对应于县实体的字段)并添加了一个对应于Prefecture(实体本身)的转换器。

@ReadingConverter
class PrefectureReadConverter : Converter<Row, Prefecture> {
    override fun convert(source: Row): Prefecture {
        val id = source.get("id", Long::class.java)
        val regionIdsString = source.get("region_ids", String::class.java)
        val mapper = ObjectMapper()
        val regionIds = mapper.readValue(regionIdsString, object : TypeReference<List<Int>>() {})
        return Prefecture(id = id?.toInt(), code = code!!, name = name!!, regionIds = regionIds)
    }
}

另外,我稍微修改了Prefecture实体的定义。

data class Prefecture(
    @Id
    val id: Int? = null,
    val regionIds: List<Int>, // eliminate wrapper class RegionIds because it's not necessary anymore
)

然后,我将注册转换器的方式改为:

@Configuration
class R2dbcConfiguration(
    private val connectionFactory: ConnectionFactory
) : AbstractR2dbcConfiguration() {
    override fun connectionFactory() = connectionFactory
    override fun getCustomConverters() = mutableListOf<Any>(PrefectureReadConverter())
}

如果您实施我在问题上发布的方式,您可能会看到错误:

org.springframework.data.mapping.MappingException: Could not read property private final boolean xxx.yyy from column zzz!
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.Byte] to type [boolean]

根据this,如果您在注册自定义转换器时不继承AbstractR2dbcConfiguration 类,似乎r2dbc 方言的预定义转换器将不可用。

为了持久化实体,我还必须实现WritingConverter

@WritingConverter
class PrefectureWriteConverter : Converter<Prefecture, OutboundRow> {
    override fun convert(source: Prefecture): OutboundRow {
        val row = OutboundRow()
        with(row) {
            put("id", Parameter.fromOrEmpty(source.id?.toLong(), Long::class.java))
            put("region_ids", Parameter.from(ObjectMapper().writeValueAsString(source.regionIds)))
        }
        return row
    }
}

并注册。

【讨论】:

    猜你喜欢
    • 2021-01-28
    • 2021-12-16
    • 2019-10-28
    • 2017-12-28
    • 2020-07-04
    • 1970-01-01
    • 2022-09-26
    • 1970-01-01
    • 2012-04-30
    相关资源
    最近更新 更多