【问题标题】:Is there a way to make a generic AttributeConverter in Micronaut Data?有没有办法在 Micronaut Data 中制作通用的 AttributeConverter?
【发布时间】:2022-06-14 00:48:45
【问题描述】:

我有很多值对象,它们基本上将 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 值对象(BarIdConverterFooBarIdConverter、...)复制转换器,但这似乎并不是真的 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)

// 编辑(解决方案):

感谢@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。我自己在回答中描述了这个选项。

【问题讨论】:

    标签: java kotlin micronaut micronaut-data


    【解决方案1】:

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

    【讨论】:

    • 非常感谢!
    【解决方案2】:

    我现在通过在this 优秀文章之后使用 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)
    

    【讨论】:

      猜你喜欢
      • 2022-12-22
      • 2017-07-19
      • 2022-08-24
      • 1970-01-01
      • 1970-01-01
      • 2017-06-08
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      相关资源
      最近更新 更多