I use Kotlin and I am trying to compare two complex objects (with multiple cycles) with JaVers. These objects use multiple Id-Properties. Therefore I created Id-Classes to have a single Id-Property for every class. In the Id-Classes I also use references to the root objects because I need to use them to create the primary key for my database.
When I compare two objects with a single change in the root object, JaVers should only list one ValueChange. But instead JaVers finds 5 changes instead (NewObject-child, ObjectRemoved-child,ReferenceChanged-child,ListChange-root,ValueChanged-root).
Trying to solve this issue, I updated my equals and hashCode methods for the child objects to check the id of the root object instead of the root object itself when calculating equality ==> root1.childList == root2.childList
returns true
.
Any ideas how I can teach JaVers that no child object has changed?
League.kt - root object
@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 - root object 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 - child object
@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 - child object 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
}
}
Update
The problem is the reference to the root object within the id of the child object. If I remove this reference from the id and move to the object itself only one change is detected by JaVers. Due to my data model I am not sure if I can remove this references in every id object. @DiffIgnore is not working within the Id-Property because it is handled as a ValueObject.