如果更改了根对象中的简单 属性,JaVers 会检测子对象的更改
JaVers detects changes in childs if simple property in root object is changed
我使用 Kotlin,我正在尝试将两个复杂对象(具有多个循环)与 JaVers 进行比较。这些对象使用多个 Id-Properties。因此,我创建了 Id-类 以便每个 class 都有一个 Id-属性。在 Id-类 中,我还使用了对根对象的引用,因为我需要使用它们来为我的数据库创建主键。
当我将两个对象与根对象中的单个更改进行比较时,JaVers 应该只列出一个 ValueChange。但是 JaVers 却发现了 5 个更改(NewObject-child、ObjectRemoved-child、ReferenceChanged-child、ListChange-root、ValueChanged-root)。
为了解决这个问题,我更新了子对象的 equals 和 hashCode 方法,以在计算相等性时检查根对象的 id 而不是根对象本身 ==> root1.childList == root2.childList
returns true
.
有什么想法可以教 JaVers 没有子对象发生变化吗?
League.kt - 根对象
@Entity
data class League(@EmbeddedId val leagueId: LeagueId? = null,
var name: String? = null,
var region: String? = null,
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
var groups: List<TeamGroup>? = null,
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
var matchDays: List<MatchDay>? = null) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as League
if (leagueId != other.leagueId) return false
if (name != other.name) return false
if (region != other.region) return false
if (groups?.map { it.teamGroupId }?.toSet() != other.groups?.map { it.teamGroupId }?.toSet()) return false
if (matchDays?.map { it.matchDayId }?.toSet() != other.matchDays?.map { it.matchDayId }?.toSet()) return false
return true
}
override fun hashCode(): Int {
var result = leagueId?.hashCode() ?: 0
result = 31 * result + (name?.hashCode() ?: 0)
result = 31 * result + (region?.hashCode() ?: 0)
result = 31 * result + (groups?.map { it.teamGroupId }?.toSet()?.hashCode() ?: 0)
result = 31 * result + (matchDays?.map { it.matchDayId }?.toSet()?.hashCode() ?: 0)
return result
}
}
LeagueId.kt - 根对象 ID
data class LeagueId(val season : String? = null, val abb : String? = null) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LeagueId
if (season != other.season) return false
if (abb != other.abb) return false
return true
}
override fun hashCode(): Int {
var result = season?.hashCode() ?: 0
result = 31 * result + (abb?.hashCode() ?: 0)
return result
}
}
TeamGroup.kt - 子对象
@Entity
data class TeamGroup(@EmbeddedId val teamGroupId: TeamGroupId? = null,
val name: String? = null,
val mode: String? = null,
val tableMode: Int? = null,
@OneToMany(mappedBy = "group", cascade = [CascadeType.ALL], orphanRemoval = true)
var teams: List<Team>? = null,
@OneToMany(mappedBy = "group", cascade = [CascadeType.ALL], orphanRemoval = true)
var matches: List<Match>? = null,
var remarks: String? = null,
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
var rows: List<Row>? = null) : Serializable {
override fun toString(): String {
return "TeamGroup(id=${teamGroupId?.id}, nr=${teamGroupId?.nr}, name=$name, mode=$mode, " +
"tableMode=$tableMode, teams=$teams, matches=$matches, remarks=$remarks, rows=$rows, " +
"league=${teamGroupId?.league?.leagueId?.season}-${teamGroupId?.league?.leagueId?.abb})"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TeamGroup
if (teamGroupId != other.teamGroupId) return false
if (name != other.name) return false
if (mode != other.mode) return false
if (tableMode != other.tableMode) return false
if (teams?.map{it.id}?.toSet() != other.teams?.map{it.id}?.toSet()) return false
if (matches?.map{it.matchId}?.toSet() != other.matches?.map{it.matchId}?.toSet()) return false
if (remarks != other.remarks) return false
if (rows?.map{it.rowId}?.toSet() != other.rows?.map{it.rowId}?.toSet()) return false
return true
}
override fun hashCode(): Int {
var result = teamGroupId?.hashCode() ?: 0
result = 31 * result + (name?.hashCode() ?: 0)
result = 31 * result + (mode?.hashCode() ?: 0)
result = 31 * result + (tableMode ?: 0)
result = 31 * result + (teams?.map{it.id}?.toSet()?.hashCode() ?: 0)
result = 31 * result + (matches?.map{it.matchId}?.toSet()?.hashCode() ?: 0)
result = 31 * result + (remarks?.hashCode() ?: 0)
result = 31 * result + (rows?.map{it.rowId}?.toSet()?.hashCode() ?: 0)
return result
}
}
TeamGroupId.kt - 子对象 ID
data class TeamGroupId(@ManyToOne val league: League? = null, val id : Int? = null, val nr : Int? = null) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TeamGroupId
if (league?.leagueId != other.league?.leagueId) return false
if (id != other.id) return false
if (nr != other.nr) return false
return true
}
override fun hashCode(): Int {
var result = league?.leagueId?.hashCode() ?: 0
result = 31 * result + (id ?: 0)
result = 31 * result + (nr ?: 0)
return result
}
}
更新
问题是在子对象的 ID 中引用了根对象。如果我从 id 中删除此引用并移动到对象本身,则 JaVers 只会检测到一个更改。由于我的数据模型,我不确定是否可以在每个 id 对象中删除此引用。 @DiffIgnore 在 Id-属性 中不起作用,因为它被作为 ValueObject 处理。
问题是由您的实体的 InstanceId 值错误引起的。
由于您有复杂的对象作为实体 ID,因此 JaVers 使用 reflectiveToString()
函数来创建 ID 的字符串表示形式。
在您的情况下,它会产生非常糟糕的结果,因为您有循环(ID 具有对拥有实体的引用)。
幸运的是,您可以使用 JaversBuider.registerValueWithCustomToString()
注册自定义 toString() 函数,例如:
@TypeName("Entity")
class Entity {
@Id Point id
String data
}
class Point {
double x
double y
String myToString() {
"("+ (int)x +"," +(int)y + ")"
}
}
def "should use custom toString function for complex Id"(){
given:
Entity entity = new Entity(id: new Point(x: 1/3, y: 4/3))
when: "default reflectiveToString function"
def javers = JaversBuilder.javers()
.build()
GlobalId id = javers.getTypeMapping(Entity).createIdFromInstance(entity)
then:
id.value() == "Entity/0.3333333333,1.3333333333"
when: "custom toString function"
javers = JaversBuilder.javers()
.registerValueWithCustomToString(Point, {it.myToString()})
.build()
id = javers.getTypeMapping(Entity).createIdFromInstance(entity)
then:
id.value() == "Entity/(0,1)"
}
另请参阅有关实体 ID 的更新文档 https://javers.org/documentation/domain-configuration/#entity-id
我使用 Kotlin,我正在尝试将两个复杂对象(具有多个循环)与 JaVers 进行比较。这些对象使用多个 Id-Properties。因此,我创建了 Id-类 以便每个 class 都有一个 Id-属性。在 Id-类 中,我还使用了对根对象的引用,因为我需要使用它们来为我的数据库创建主键。
当我将两个对象与根对象中的单个更改进行比较时,JaVers 应该只列出一个 ValueChange。但是 JaVers 却发现了 5 个更改(NewObject-child、ObjectRemoved-child、ReferenceChanged-child、ListChange-root、ValueChanged-root)。
为了解决这个问题,我更新了子对象的 equals 和 hashCode 方法,以在计算相等性时检查根对象的 id 而不是根对象本身 ==> root1.childList == root2.childList
returns true
.
有什么想法可以教 JaVers 没有子对象发生变化吗?
League.kt - 根对象
@Entity
data class League(@EmbeddedId val leagueId: LeagueId? = null,
var name: String? = null,
var region: String? = null,
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
var groups: List<TeamGroup>? = null,
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
var matchDays: List<MatchDay>? = null) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as League
if (leagueId != other.leagueId) return false
if (name != other.name) return false
if (region != other.region) return false
if (groups?.map { it.teamGroupId }?.toSet() != other.groups?.map { it.teamGroupId }?.toSet()) return false
if (matchDays?.map { it.matchDayId }?.toSet() != other.matchDays?.map { it.matchDayId }?.toSet()) return false
return true
}
override fun hashCode(): Int {
var result = leagueId?.hashCode() ?: 0
result = 31 * result + (name?.hashCode() ?: 0)
result = 31 * result + (region?.hashCode() ?: 0)
result = 31 * result + (groups?.map { it.teamGroupId }?.toSet()?.hashCode() ?: 0)
result = 31 * result + (matchDays?.map { it.matchDayId }?.toSet()?.hashCode() ?: 0)
return result
}
}
LeagueId.kt - 根对象 ID
data class LeagueId(val season : String? = null, val abb : String? = null) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LeagueId
if (season != other.season) return false
if (abb != other.abb) return false
return true
}
override fun hashCode(): Int {
var result = season?.hashCode() ?: 0
result = 31 * result + (abb?.hashCode() ?: 0)
return result
}
}
TeamGroup.kt - 子对象
@Entity
data class TeamGroup(@EmbeddedId val teamGroupId: TeamGroupId? = null,
val name: String? = null,
val mode: String? = null,
val tableMode: Int? = null,
@OneToMany(mappedBy = "group", cascade = [CascadeType.ALL], orphanRemoval = true)
var teams: List<Team>? = null,
@OneToMany(mappedBy = "group", cascade = [CascadeType.ALL], orphanRemoval = true)
var matches: List<Match>? = null,
var remarks: String? = null,
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
var rows: List<Row>? = null) : Serializable {
override fun toString(): String {
return "TeamGroup(id=${teamGroupId?.id}, nr=${teamGroupId?.nr}, name=$name, mode=$mode, " +
"tableMode=$tableMode, teams=$teams, matches=$matches, remarks=$remarks, rows=$rows, " +
"league=${teamGroupId?.league?.leagueId?.season}-${teamGroupId?.league?.leagueId?.abb})"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TeamGroup
if (teamGroupId != other.teamGroupId) return false
if (name != other.name) return false
if (mode != other.mode) return false
if (tableMode != other.tableMode) return false
if (teams?.map{it.id}?.toSet() != other.teams?.map{it.id}?.toSet()) return false
if (matches?.map{it.matchId}?.toSet() != other.matches?.map{it.matchId}?.toSet()) return false
if (remarks != other.remarks) return false
if (rows?.map{it.rowId}?.toSet() != other.rows?.map{it.rowId}?.toSet()) return false
return true
}
override fun hashCode(): Int {
var result = teamGroupId?.hashCode() ?: 0
result = 31 * result + (name?.hashCode() ?: 0)
result = 31 * result + (mode?.hashCode() ?: 0)
result = 31 * result + (tableMode ?: 0)
result = 31 * result + (teams?.map{it.id}?.toSet()?.hashCode() ?: 0)
result = 31 * result + (matches?.map{it.matchId}?.toSet()?.hashCode() ?: 0)
result = 31 * result + (remarks?.hashCode() ?: 0)
result = 31 * result + (rows?.map{it.rowId}?.toSet()?.hashCode() ?: 0)
return result
}
}
TeamGroupId.kt - 子对象 ID
data class TeamGroupId(@ManyToOne val league: League? = null, val id : Int? = null, val nr : Int? = null) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TeamGroupId
if (league?.leagueId != other.league?.leagueId) return false
if (id != other.id) return false
if (nr != other.nr) return false
return true
}
override fun hashCode(): Int {
var result = league?.leagueId?.hashCode() ?: 0
result = 31 * result + (id ?: 0)
result = 31 * result + (nr ?: 0)
return result
}
}
更新
问题是在子对象的 ID 中引用了根对象。如果我从 id 中删除此引用并移动到对象本身,则 JaVers 只会检测到一个更改。由于我的数据模型,我不确定是否可以在每个 id 对象中删除此引用。 @DiffIgnore 在 Id-属性 中不起作用,因为它被作为 ValueObject 处理。
问题是由您的实体的 InstanceId 值错误引起的。
由于您有复杂的对象作为实体 ID,因此 JaVers 使用 reflectiveToString()
函数来创建 ID 的字符串表示形式。
在您的情况下,它会产生非常糟糕的结果,因为您有循环(ID 具有对拥有实体的引用)。
幸运的是,您可以使用 JaversBuider.registerValueWithCustomToString()
注册自定义 toString() 函数,例如:
@TypeName("Entity")
class Entity {
@Id Point id
String data
}
class Point {
double x
double y
String myToString() {
"("+ (int)x +"," +(int)y + ")"
}
}
def "should use custom toString function for complex Id"(){
given:
Entity entity = new Entity(id: new Point(x: 1/3, y: 4/3))
when: "default reflectiveToString function"
def javers = JaversBuilder.javers()
.build()
GlobalId id = javers.getTypeMapping(Entity).createIdFromInstance(entity)
then:
id.value() == "Entity/0.3333333333,1.3333333333"
when: "custom toString function"
javers = JaversBuilder.javers()
.registerValueWithCustomToString(Point, {it.myToString()})
.build()
id = javers.getTypeMapping(Entity).createIdFromInstance(entity)
then:
id.value() == "Entity/(0,1)"
}
另请参阅有关实体 ID 的更新文档 https://javers.org/documentation/domain-configuration/#entity-id