如果更改了根对象中的简单属性,JaVers会检测子级中的更改

时间:2017-12-27 23:31:26

标签: java kotlin javers

我使用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处理的。

1 个答案:

答案 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

的更新文档