You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单数据库表中存储Kotlin泛型类型的应用配置

问题分析与修复方案

错误原因

Hibernate无法处理泛型实体中未绑定的类型参数T——运行时类型擦除后,Hibernate不知道该将value字段映射到数据库的哪种数据类型,也没有明确的目标实体信息,因此抛出AnnotationException。

修复方案:用JSON序列化实现多类型配置存储(推荐)

这种方案通过将任意类型的配置值序列化为JSON字符串存储,读取时反序列化为对应类型,同时新增字段记录值的类型,确保类型安全。

1. 调整实体类

引入hibernate-types依赖(适配你的Hibernate版本,比如com.vladmihalcea:hibernate-types-55),然后修改实体类:

@Entity
@Table(name = "table_application_settings")
data class ApplicationSetting(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "application_setting_id")
    val id: Long? = null,

    @Column(name = "application_setting_name", unique = true)
    var name: String = "",

    // 存储JSON格式的配置值,用TEXT类型避免长度限制
    @Column(name = "application_setting_value", columnDefinition = "TEXT")
    @Type(JsonType::class)
    var value: Any? = null,

    // 记录值的全类名,用于反序列化时确定类型
    @Column(name = "application_setting_value_type")
    var valueType: String = ""
)

2. 简化Repository

去掉泛型声明,直接使用调整后的实体类:

@Repository
interface ApplicationSettingsRepository : JpaRepository<ApplicationSetting, Long> {
    fun findByName(name: String): ApplicationSetting?
}

3. 优化Service层逻辑

在Service中处理类型转换和类型标识的维护:

@Service
@Transactional
class ApplicationSettingsServiceImpl(
    private val applicationSettingsRepository: ApplicationSettingsRepository,
) : ApplicationSettingsService {

    override fun saveBooleanApplicationSetting(name: String, value: Boolean): ApplicationSetting {
        return saveOrUpdateSetting(name, value, Boolean::class.java.name)
    }

    override fun saveIntegerApplicationSetting(name: String, value: Int): ApplicationSetting {
        return saveOrUpdateSetting(name, value, Int::class.java.name)
    }

    override fun getBooleanSetting(name: String): Boolean? {
        return getSettingValue(name) as? Boolean
    }

    override fun getIntegerSetting(name: String): Int? {
        return getSettingValue(name) as? Int
    }

    override fun getAllApplicationSettings(): MutableList<ApplicationSetting> {
        return applicationSettingsRepository.findAll()
    }

    // 通用保存/更新方法
    private fun saveOrUpdateSetting(name: String, value: Any, type: String): ApplicationSetting {
        val setting = applicationSettingsRepository.findByName(name) ?: ApplicationSetting()
        setting.name = name
        setting.value = value
        setting.valueType = type
        return applicationSettingsRepository.save(setting)
    }

    // 通用获取值方法
    private fun getSettingValue(name: String): Any? {
        return applicationSettingsRepository.findByName(name)?.value
    }
}

替代方案:针对特定类型使用AttributeConverter

如果只需要支持有限的基础类型(如Boolean、Int),可以为每种类型编写AttributeConverter,将值转换为数据库字符串存储:

  1. 编写Boolean转换器:
@Converter(autoApply = false)
class BooleanToStringConverter : AttributeConverter<Boolean, String> {
    override fun convertToDatabaseColumn(attribute: Boolean?): String? = attribute?.toString()
    override fun convertToEntityAttribute(dbData: String?): Boolean? = dbData?.toBoolean()
}
  1. 但这种方式需要实体类放弃泛型,通过类型字段配合@Convert注解指定转换器,灵活性远不如JSON序列化方案,因此仅推荐在类型极少的场景使用。

内容的提问来源于stack exchange,提问作者JamieRhys

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 17:40:55