Вопрос:

JaVers detects changes in childs if simple property in root object is changed

java kotlin javers

336 просмотра

1 ответ

3 Репутация автора

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.

Когда я сравниваю два объекта с одним изменением корневого объекта, JaVers должен перечислять только один ValueChange. Но вместо этого JaVers находит 5 изменений (NewObject-child, ObjectRemoved-child, ReferenceChanged-child, ListChange-root, ValueChanged-root). Пытаясь решить эту проблему, я обновил свои методы equals и hashCode для дочерних объектов, чтобы проверять идентификатор корневого объекта вместо самого корневого объекта при вычислении равенства ==> 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 - идентификатор корневого объекта

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 - идентификатор дочернего объекта

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
    }
}

Обновить

Проблема заключается в ссылке на корневой объект внутри идентификатора дочернего объекта. Если я удаляю эту ссылку из идентификатора и перемещаюсь к самому объекту, JaVers обнаруживает только одно изменение. Из-за моей модели данных я не уверен, смогу ли я удалить эти ссылки в каждом объекте id. @DiffIgnore не работает в свойстве Id, поскольку оно обрабатывается как ValueObject.

Автор: asuras Источник Размещён: 27.12.2017 11:31

Ответы (1)


0 плюса

2068 Репутация автора

Решение

Проблема вызвана неправильными значениями InstanceId ваших сущностей. Поскольку у вас есть сложные объекты в качестве идентификаторов сущностей, JaVers использует reflectiveToString()функцию для создания строковых представлений идентификаторов. В вашем случае это дает действительно плохие результаты, потому что у вас есть циклы (ID имеет ссылку на владельца сущности).

К счастью, вы можете зарегистрировать пользовательскую функцию toString (), используя JaversBuider.registerValueWithCustomToString(), например:

@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)"
}

См. Также обновленный документ об идентификаторе организации https://javers.org/documentation/domain-configuration/#entity-id.

Автор: Bartek Walacik Размещён: 06.01.2018 04:21
Вопросы из категории :
32x32