我使用Kotlin,我试图用JaVers比较两个复杂的对象(有多个周期)。这些对象使用多个Id属性。因此,我创建了Id-Classes,为每个类都有一个Id-Property。在Id-Classes中,我还使用对根对象的引用,因为我需要使用它们来为我的数据库创建主键。
当我将两个对象与根对象中的单个更改进行比较时,JaVers应该只列出一个ValueChange。但相反,JaVers发现了5个更改(NewObject-child,ObjectRemoved-child,ReferenceChanged-child,ListChange-root,ValueChanged-root)。
为了解决这个问题,我更新了子对象的equals和hashCode方法,以便在计算相等性时检查根对象的id而不是根对象本身==> root1.childList == root2.childList
返回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-Property中不起作用,因为它是作为ValueObject处理的。
答案 0 :(得分:0)
问题是由您的实体的错误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
的更新文档