java - 使用 Axon CQRS 更改事件类别

标签 java spring-boot kotlin cqrs axon

在 axon 中测试事件迁移时,我发现具有默认值的新字段实际上设置为 null。 向以前的事件实际添加字段(具有默认值)的好方法是什么? 我不希望 MyEventV2 的值是强制的(无默认值),并且不希望指定“upcaster”。

首先,我们有这个事件,它被发布并存储在 axon 服务器中:

data class MyEvent(
    val someId: String,
    val name: String
)

后来我们更改了MyEvent,我们给出了一个默认值以保持与之前持久化事件的兼容性,我们期望旧事件具有这个默认值。 真实结果:接收旧事件时'newField'为null。

data class MyEvent(
    val someId: String,
    val name: String,
    val newField: String = "defaultValueButWillBeNullInPreviousEvents... -> would need it to have the actual default value"
)

我使用 axon 服务器、axon 4.2、spring boot 2.2.0 进行了测试,所有默认值均来自 axon boot starter、Jackson 2.10.0

我们是否应该使用类似的东西(但根本不是很好......):

data class MyEvent(
    val someId: String,
    val name: String,
    private val newFieldV2: String? = null
) {
    val newField: String
        get() = newFieldV2 ?: "my default value"
}

我还没有检查 axon 如何从事件存储中重建事件,但我猜它是通过字段访问(反射)而不是构造函数来实现的。 但奇怪的是,数据类没有提供默认的空构造函数......

编辑: Axon 默认使用 XStreamSerializer,我已切换配置以使用 Jackson,但问题仍然存在,并且 Jackson kotlin 模块已注册。

编辑2: 更新到 Jackson 2.10.1 (参见 issue )应该修复,但由于某种原因我收到另一个错误“com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造 org.x.x.MyEvent 的实例(没有创建者,如默认构造,存在):无法从对象值反序列化(没有基于委托(delegate)或属性的创建者)”。

如果添加“ParameterNamesModule”会出现错误,如果不添加则事件的默认值仍然为null。

编辑3: 我编写了测试,但 axon 事件 org.axonframework.serialization.json.JacksonSerializer 仍然有错误

@ExperimentalStdlibApi
class JsonDefaultValueTest {

    private lateinit var mapper1: ObjectMapper
    private lateinit var mapper2: ObjectMapper
    private lateinit var mapper3: ObjectMapper
    private lateinit var mapper4: ObjectMapper
    private lateinit var mapper5: ObjectMapper
    private lateinit var mapper6: ObjectMapper

    @BeforeEach
    fun setup() {
        mapper1 = Jackson2ObjectMapperBuilder.json()
            .build()
        mapper2 = ObjectMapper()
        mapper3 = ObjectMapper().registerModule(KotlinModule())
        mapper4 = ObjectMapper().registerModule(KotlinModule(nullisSameAsDefault = true))
        mapper5 = ObjectMapper().registerModule(ParameterNamesModule())
        mapper6 = ObjectMapper()
            .registerModule(ParameterNamesModule())
            .registerModule(KotlinModule())
            .registerModule(Jdk8Module())
            .registerModule(JavaTimeModule())
    }

    @Test
    fun `only s1 passed with mapper 1`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper1.readValue<MyEvent>(json)

        val event2 = mapper1.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
    }

    @Test
    fun `only s1 passed with mapper 2`() {
        val json = """{"s1":"only s1"}"""

        assertThrows<InvalidDefinitionException> { mapper2.readValue<MyEvent>(json) }

        assertThrows<InvalidDefinitionException> { mapper2.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray()) }

    }

    @Test
    fun `only s1 passed with mapper 3`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper3.readValue<MyEvent>(json)

        val event2 = mapper3.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
        Assertions.assertEquals(expected, event2)
    }

    @Test
    fun `only s1 passed with mapper 4`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper4.readValue<MyEvent>(json)

        val event2 = mapper4.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
        Assertions.assertEquals(expected, event2)
    }

    @Test
    fun `only s1 passed with mapper 5`() {
        val json = """{"s1":"only s1","s2":null}"""

        assertThrows<ValueInstantiationException> { mapper5.readValue<MyEvent>(json) }

        assertThrows<ValueInstantiationException> { mapper5.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray()) }

    }

    @Test
    fun `only s1 passed with mapper 6`() {
        val json = """{"s1":"only s1"}"""

        val event = mapper6.readValue<MyEvent>(json)

        val event2 = mapper6.readerFor(MyEvent::class.java).readValue<MyEvent>(json.encodeToByteArray())

        val expected = MyEvent(
            "only s1",
            aInt = 0
        )

        Assertions.assertEquals(expected, event)
        Assertions.assertEquals(expected, event2)
    }

    data class MyEvent(
        val s1: String,
        val aInt: Int,
        val s2: String = "my default"
    )
}

最佳答案

这似乎是 Jackson-Kotlin 问题。您需要显式配置 Jackson 将“null”(或缺少字段)视为“使用默认值”。

按照 https://github.com/FasterXML/jackson-module-kotlin/issues/130#issuecomment-546625625 中的推荐,你应该使用:

ObjectMapper().registerModule(KotlinModule(nullisSameAsDefault = true)

关于java - 使用 Axon CQRS 更改事件类别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59191591/

相关文章:

kotlin - 函数调用后的方括号

java - 如何更有效地通过 http 下载大文件?

Java:Clob 到 byte[]

java - 区分 .xls 和 .doc 文件的正确方法?

java - 在 Jackson/Spring Boot 中测试自定义 Json Deserializer

java - 使用使用 kotlin-maven-plugin 构建的 jar 中的 Kotlin 类

java - 基于命令行参数注入(inject)实现

kotlin - 为什么 BiPredicate.test 方法在 Kotlin 中不可见?

java - 正则表达式异常

Java Math.incrementExact(int a) 目的