Spring Data R2DBC自定义转换不适用于json数组



我正在尝试对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>
)

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

@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的自定义编解码器,但并不是不能,因为与此功能相关的修复程序尚未发布。

我正在使用

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

我是遗漏了什么还是误解了什么?

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

首先,我删除了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]

根据这一点,如果在注册自定义转换器时不继承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
}
}

并将其注册。

最新更新