是否有办法使一个通用的attributecconverter在微aut数据?



我有很多值对象,它们基本上将uuid表示为实体/聚合的主要标识符:

@Singleton
class FooIdConverter : AttributeConverter<FooId, UUID> {
override fun convertToPersistedValue(
@Nullable entityValue: FooId?, @NonNull context: ConversionContext
): UUID? {
return entityValue?.id
}
override fun convertToEntityValue(
persistedValue: UUID?, context: ConversionContext
): FooId? {
return if (persistedValue == null) null else FooId(persistedValue)
}
}
@TypeDef(type = DataType.UUID, converter = FooIdConverter::class)
data class FooId(val id: UUID)

当然,我可以为每个其他ID值对象(BarIdConverter,FooBarIdConverter,…)复制转换器,但这似乎不是真正的DRY。

在Kotlin中是否有更好的方法来做到这一点?

我试过这样做,但它不工作:

abstract class EntityId(open val id: UUID)
fun <Id : EntityId> createConverterFor(clazz: KClass<Id>): KClass<out AttributeConverter<Id, UUID>> {
return object : AttributeConverter<Id, UUID> {
override fun convertToPersistedValue(entityValue: Id?, context: ConversionContext): UUID? {
return entityValue?.id
}
override fun convertToEntityValue(persistedValue: UUID?, context: ConversionContext): Id? {
return if (persistedValue == null) null else clazz.primaryConstructor!!.call(
persistedValue
)
}
}::class
}
val ProjectIdConverter = createConverterFor(FooId::class)
@TypeDef(type = DataType.UUID, converter = ProjectIdConverter)
data class FooId(override val id: UUID) : EntityId(id)

//EDIT (Solution):

感谢@Denis,我现在有两个解决方案。第一个也是最直接的:

@Singleton
class EntityIdConverter() : AttributeConverter<EntityId, UUID> {
override fun convertToPersistedValue(
@Nullable entityValue: EntityId?, @NonNull context: ConversionContext
): UUID? {
return entityValue?.id
}
override fun convertToEntityValue(
persistedValue: UUID?, context: ConversionContext
): EntityId? {
val ctx = context as ArgumentConversionContext<*>
return if (persistedValue == null) {
null
} else {
ctx.argument.type.getDeclaredConstructor(UUID::class.java)
.newInstance(persistedValue) as EntityId
}
}
}
@TypeDef(type = DataType.UUID, converter = EntityIdConverter::class)
abstract class EntityId(open val id: UUID)
data class ProjectId(override val id: UUID) : EntityId(id)

第二种选择是使用KSP生成AttributeConverter。这个选项是我自己在回答中描述的。

您可以使用应该是ArgumentConversionContext类型的上下文来进行共享转换并识别所需的类型,getArgument将返回该类型。

我现在通过使用KSP根据这篇优秀的文章生成转换器实现来解决这个问题。

代码生成器看起来像这样:

override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation("package.of.annotation")
.filterIsInstance<KSClassDeclaration>()
if (!symbols.iterator().hasNext()) {
return emptyList()
}
symbols.forEach {
val file: OutputStream = codeGenerator.createNewFile(
dependencies = Dependencies(false, it.containingFile!!),
packageName = it.packageName.asString(),
fileName = "${it.simpleName.asString()}Converter"
)
file += "package ${it.packageName.asString()}n"
val name = it.simpleName.asString()
file += """
import io.micronaut.core.annotation.NonNull
import io.micronaut.core.annotation.Nullable
import io.micronaut.core.convert.ConversionContext
import io.micronaut.data.model.runtime.convert.AttributeConverter
import java.util.*

class ${name}Converter : AttributeConverter<$name, UUID> {
override fun convertToPersistedValue(
@Nullable entityValue: $name?,
@NonNull context: ConversionContext
): UUID? {
return entityValue?.id
}
override fun convertToEntityValue(persistedValue: UUID?, context: ConversionContext): $name? {
return if (persistedValue == null) null else $name(persistedValue)
}
}
""".trimIndent()
file.close()
}
return symbols.filterNot { it.validate() }.toList()
}

我的值对象是这样的

@ValueId
@TypeDef(type = DataType.UUID, converter = ProjectIdConverter::class)
data class ProjectId(val id: UUID)

相关内容

  • 没有找到相关文章

最新更新