Равные для постоянных объектов - PullRequest
1 голос
/ 25 октября 2011

Существует хорошо известная проблема с реализацией equals () (и hashCode (), я буду говорить только о equals ()) для персистентного объекта с управляемым идентификатором базы данных.Новый объект не хранится в базе данных, поэтому не имеет идентификатора базы данных, поэтому его поле «id» имеет значение null (или 0, если это тип примитива).

Если равен id, то он будет считать все новыеобъекты равны, и как только он получает идентификатор, хэш-код изменяется, поэтому, если он уже был в коллекции, чувствительной к хэшу, он не будет найден.

Одно из решений - использование бизнес-ключа, но иногда все, кроме суррогатного идентификатора, является изменчивым.Другое решение состоит в том, чтобы сгенерировать (еще один, или использовать его в качестве идентификатора данных) суррогатный идентификатор при создании объекта.

Подход, который я не видел, состоит в том, чтобы использовать идентификатор в равных и сделать равными (и hashCode ()) сбой (сгенерировать IllegalStateException), когда идентификатор равен нулю.(и задокументируйте это поведение) Таким образом, он все еще не может быть в коллекциях хэшей, но не может быть случайно помещен туда.И для помещения его в коллекции, когда без идентификатора, может быть использована некоторая оболочка.Это хорошая / плохая идея?Есть ли у него скрытые проблемы?

Как указал Кан, если дочерние объекты должны быть помещены в свойство Set и сохранены вместе с их родителем, невозможность поместить объекты в Set до того, как они будут сохранены, является большой проблемой (а TreeSet не делает этого).справка, потому что он использует equals (), даже если он не использует hashCode ()).Я в основном использую список для дочерних объектов, поэтому его не нужно манифестировать, но это определенно проблема.

Ответы [ 2 ]

1 голос
/ 25 октября 2011

Я всегда использую автоматически сгенерированный идентификатор, и у меня никогда не было проблем.При создании экземпляра вы могли бы принудительно сохранить объект, используя также уровень обслуживания / фабрику.

Я думаю, что вероятность изменения любого другого поля (составления ключа biz) гораздо более вероятна, чем использование непостоянного объекта.в хэш-карте, а затем сохраняется в в то же время , что приводит к сбою при поиске.

Эта проблема, imho, несколько слишком проанализирована .Автоматически генерируемый идентификатор часто является единственным тестом, который я хочу сделать на равенство, во многих случаях больше ничего не имеет смысла.Я использую подход, который заключается в том, что если используется непостоянный объект / сравнивается, проблема заключается в бизнес-логике, а не в базовых методах equals / hashcode

Чтобы конкретно ответить на идею незаконного освобождения, генерируя исключение, когда объектыне равны и / или не были сохранены, кажется довольно драматичным.

0 голосов
/ 25 октября 2011

Я использую следующий код. Он охватывает большинство случаев и может использоваться для всех случаев, которые, как мне кажется, могут возникнуть при использовании ORM.

public class VersionedEntity
{
        private static final long serialVersionUID=1L;
        private Long id;
        private long version;
        @Transient
        private int hashCode;
...
        public void setId(final Long id)
        {
            if(this.id != null && !this.id.equals(id))
                throw new IllegalArgumentException(this+" has an ID already, cannot change it to "+id);
            this.id = id;
        }
        @Override
        public String toString() {
            return getClass().getName()+'#'+getId();
        }

        public boolean equals(final Object o)
        {
            if (this==o) return true;
            if (!(o instanceof VersionedEntity))
                return false;
            final VersionedEntity entity=(VersionedEntity) o;
            final Long id1 = entity.getId();
            final Long id2 = getId();
            if(id1==null && id2==null)
                return super.equals(o);
            return id1 != null
                   && id2 != null
                   && id2.equals(id1);

        }

        public int hashCode()
        {
            if(hashCode == 0)
            {
                hashCode = id != null ? id.hashCode() : super.hashCode();
                if(hashCode == 0)
                    hashCode = 42;
            }
            return hashCode;
        }
}

Вы должны иметь возможность использовать коллекции до назначения идентификатора. Если вы хотите создать объект со свойством Set, которое содержит пакет других объектов, вы должны добавить их как элементы к Set.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...