Java 事件类随Axon CQR而变化

Java 事件类随Axon CQR而变化,java,spring-boot,kotlin,cqrs,axon,Java,Spring Boot,Kotlin,Cqrs,Axon,在axon中测试事件迁移时,我发现具有默认值的新字段实际上设置为null。 向以前的事件实际添加具有默认值的字段的好方法是什么? 我不希望有一个MyEventV2,其中的值是强制性的,没有默认值,也不希望指定一个上传器 首先,我们有这个事件,它被发布并存储在axon server中: data class MyEvent( val someId: String, val name: String ) 稍后我们更改MyEvent,我们提供一个默认值以保持与以前事件的兼容性,我们希

在axon中测试事件迁移时,我发现具有默认值的新字段实际上设置为null。 向以前的事件实际添加具有默认值的字段的好方法是什么? 我不希望有一个MyEventV2,其中的值是强制性的,没有默认值,也不希望指定一个上传器

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

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,查看哪个应该修复,但由于某些原因,我收到另一个错误com.fasterxml.Jackson.databind.exc.InvalidDefinitionException:无法构造org.x.x.MyEvent的实例没有创建者(如默认构造)存在:无法从对象值反序列化没有委托或基于属性的创建者

如果添加ParameterNamesModule,则会出现错误;如果未添加,则事件的默认值仍为null

编辑3: 我编写了测试,但axon在event 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 ObjectMapper配置不正确。为了使其与Kotlin特定功能(如默认值)配合使用,您需要注册Kotlin模块:


这听起来像是用于反序列化事件的Jackson ObjectMapper配置不正确。为了使其与Kotlin特定功能(如默认值)配合使用,您需要注册Kotlin模块:


这似乎是杰克逊·科特林的问题。您需要显式配置杰克逊以考虑NULL或没有字段作为默认值。

如中所建议的,您应该使用:

ObjectMapper.registerModuleKotlinModulenullisSameAsDefault=true


这似乎是杰克逊·科特林的问题。您需要显式配置杰克逊以考虑NULL或没有字段作为默认值。

如中所建议的,您应该使用:

ObjectMapper.registerModuleKotlinModulenullisSameAsDefault=true


我在maven的依赖项中有jackson模块kotlin,因此它应该使用spring boot自动注册。这可能是Axon使用XStreamSerializer的默认值的问题,但不确定Axon中的默认值是XStream,我将事件切换到jackson,但对于具有默认值的字段,仍然得到null。在调试模式下,我发现EventBuffer具有jackson serializer字段,其中注册的模块有:com.fasterxml.jackson.datatype.jdk8.Jdk8Module、com.fasterxml.jackson.datatype.jsr310.JavaTimeModule、com.fasterxml.jackson.module.kotlin.KotlinModule、com.fasterxml.jackson.module.parameternames.ParameterNamesModule、,org.springframework.boot.jackson.JsonComponentModule我在maven的依赖项中有jackson模块kotlin,因此应该使用spring boot自动注册它。使用XStreamSerializer的Axon的默认值可能有问题,但不确定Axon中的默认值是XStream,对于事件,我切换到Jackson,但对于具有默认值的字段,仍然得到null。在调试模式下,我发现EventBuffer具有jackson serializer字段,其中注册的模块有:com.fasterxml.jackson.datatype.jdk8.Jdk8Module、com.fasterxml.jackson.datatype.jsr310.JavaTimeModule、com.fasterxml.jackson.module.kotlin.KotlinModule、com.fasterxml.jackson.module.parameternames.ParameterNamesModule、,在本期之前,我看到了org.springframework.boot.jackson.JsonComponentModule,由于某种原因,我在源代码中没有看到nullisSameAsDefault,这次我再次检查并看到了它。现在我得到了这个错误:com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造org.x.MyEvent的实例没有创建者,就像默认构造一样,存在:无法从对象值反序列化无委托或基于属性的创建者然后根据我通过Jackson2ObjectMapperBuilderCustomizer自定义的模块,我不会得到此错误,但会得到默认字段的空值,即使使用KotlinModulenullisSameAsDefault=True和我的编辑3,我们看到jackson可以在使用kotlin默认值的情况下正常工作。我应该在axon github中创建问题吗?轴突的JacksonSerializer似乎并没有像预期的那样工作。我没有配置
作为一个bean对象映射器,我甚至尝试创建JacksonSerializerI在本期之前看到的自己,由于某种原因,我在源代码中没有看到nullisSameAsDefault,我再次检查并看到了它。现在我得到了这个错误:com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造org.x.MyEvent的实例没有创建者,就像默认构造一样,存在:无法从对象值反序列化无委托或基于属性的创建者然后根据我通过Jackson2ObjectMapperBuilderCustomizer自定义的模块,我不会得到此错误,但会得到默认字段的空值,即使使用KotlinModulenullisSameAsDefault=True和我的编辑3,我们看到jackson可以在使用kotlin默认值的情况下正常工作。我应该在axon github中创建问题吗?轴突的JacksonSerializer似乎并没有像预期的那样工作。我确实配置了一个bean对象映射器,甚至尝试自己创建JacksonSerializer
val mapper = ObjectMapper().registerModule(KotlinModule())