JOOQ:如何将JSONB列映射到Kotlin数据类的List字段?
问题分析
报错的核心原因是:JOOQ默认将jsonb列映射为JSONB类型,当调用record.into(TrackedEvent::class.java)时,无法自动将JSON数组转换为List<Metadata>,而是解析成了LinkedHashMap的集合,Jackson序列化时因类型不匹配抛出异常。使用Array能正常运行是因为JOOQ对具体数组类型的映射支持更完善,但List作为接口需要额外配置类型转换逻辑。
解决方案
方案1:自定义JOOQ类型转换器(推荐)
通过自定义Converter实现JSONB与List<Metadata>的双向自动转换,一劳永逸解决映射问题,无需手动处理JSON序列化/反序列化。
1. 实现Converter类
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.core.type.TypeReference import org.jooq.Converter import org.jooq.JSONB class MetadataListConverter : Converter<JSONB, List<Metadata>> { private val objectMapper = ObjectMapper().registerModule(KotlinModule()) // 从数据库JSONB转为List<Metadata> override fun from(databaseObject: JSONB?): List<Metadata> { return databaseObject?.let { objectMapper.readValue(it.data(), object : TypeReference<List<Metadata>>() {}) } ?: emptyList() } // 从List<Metadata>转为数据库JSONB override fun to(userObject: List<Metadata>?): JSONB { return userObject?.let { JSONB.jsonb(objectMapper.writeValueAsString(it)) } ?: JSONB.jsonb("[]") } override fun fromType(): Class<JSONB> = JSONB::class.java override fun toType(): Class<List<Metadata>> = List::class.java as Class<List<Metadata>> }
2. 配置JOOQ应用转换器
方式A:代码生成阶段配置(适用于自动生成的表对象)
在JOOQ代码生成的XML配置中添加强制类型映射:
<forcedTypes> <forcedType> <userType>java.util.List<com.my.project.Metadata></userType> <converter>com.my.project.MetadataListConverter</converter> <includeExpression>tracked_event\.metadata</includeExpression> </forcedType> </forcedTypes>
方式B:手动表对象配置(适用于手动定义的表结构)
在TRACKED_EVENT表的字段定义中指定转换器:
import org.jooq.Field import org.jooq.impl.SQLDataType val METADATA: Field<List<Metadata>> = Field("metadata", SQLDataType.JSONB.asConvertedDataType(MetadataListConverter()))
3. 简化插入代码
配置完成后,无需手动处理JSON转换,直接使用JOOQ的自动映射:
fun createTrackedEvent(trackedEvent: TrackedEvent): TrackedEvent { val record = dslContext.newRecord(TRACKED_EVENT, trackedEvent) record.store() return record.into(TrackedEvent::class.java) }
方案2:手动转换返回结果(快速临时方案)
若不想修改JOOQ配置,可在存储后手动将JSONB解析为List<Metadata>,再构建TrackedEvent对象:
fun createTrackedEvent(trackedEvent: TrackedEvent): TrackedEvent { val record = dslContext.newRecord(TRACKED_EVENT, trackedEvent) record.metadata = JSONB.jsonb(objectMapper.writeValueAsString(trackedEvent.metadata)) record.store() // 手动解析JSONB为List<Metadata> val metadataList = objectMapper.readValue( record.metadata.data(), object : TypeReference<List<Metadata>>() {} ) return TrackedEvent( id = record.id, userId = record.userId, // 填充其他字段 metadata = metadataList ) }
内容的提问来源于stack exchange,提问作者Lucas Costa
相关产品推荐
相关产品推荐

