【发布时间】:2019-12-05 09:06:15
【问题描述】:
在 axon 中测试事件迁移时,我发现具有默认值的新字段实际上设置为 null。 将字段(具有默认值)实际添加到先前事件的好方法是什么? 我不想有一个 MyEventV2 值是强制性的(没有默认值)并且不想指定一个“upcaster”。
首先,我们有这个事件,它被发布并存储在轴突服务器中:
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 server、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 的实例(没有创作者,如默认构造, 存在):无法从 Object 值反序列化(没有基于委托或属性的 Creator)”。
如果添加“ParameterNamesModule”会出现错误,如果没有添加,则事件的默认值仍然为空。
编辑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"
)
}
【问题讨论】:
标签: java spring-boot kotlin cqrs axon