Подтвердить что ты не робот

Равномерность объектов в контексте hibernate/webapp

Как вы обрабатываете равенство объектов для java-объектов, управляемых спящим режимом? В книге "Спящий режим в действии" они говорят, что нужно отдавать предпочтение бизнес-ключам над суррогатными ключами.
В большинстве случаев у меня нет бизнес-ключа. Подумайте о адресах, сопоставленных с человеком. Адреса хранятся в наборе и отображаются в Wicket RefreshingView (с помощью стратегии ReuseIfEquals).

Я мог либо использовать суррогатный идентификатор, либо использовать все поля в функциях equals() и hashCode().
Проблема в том, что эти поля изменяются в течение жизни ob объекта. Либо потому, что пользователь ввел некоторые данные или изменения идентификатора из-за слияния JPA(), вызванного внутри фильтра OSIV (Open Session in View).

Мое понимание контракта equals() и hashCode() заключается в том, что они не должны меняться в течение всего жизненного цикла объекта.

Что я пробовал до сих пор:

  • equals() на основе hashCode(), который использует идентификатор базы данных (или super.hashCode(), если id равен null). Проблема: новые адреса начинаются с нулевого идентификатора, но получают идентификатор, когда он привязан к человеку, и этот человек объединяется() (повторно присоединяется) в osiv-фильтр.
  • lazy вычислить хэш-код при первом вызове hashCode() и сделать этот хэш-код @Transitional. Не работает, так как merge() возвращает новый объект, а hashcode не копируется.

Мне нужен идентификатор, который присваивается при создании объекта, я думаю. Какие здесь варианты? Я не хочу вводить некоторые дополнительные постоянные свойства. Есть ли способ явно указать JPA для назначения идентификатора объекту?

Привет

4b9b3361

Ответ 1

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

Использование всех свойств (кроме идентификатора базы данных) также не является хорошей идеей, поскольку все свойства просто не являются частью идентификатора.

Таким образом, предпочтительным (и правильным) способом реализации равенства является использование бизнес-ключа, как описано в Java Persistence with Hibernate:

Реализация равенства с бизнес-ключом

Чтобы найти решение, которое мы рекомендуем, вам нужно понять понятие бизнес-ключ. Бизнес-ключ - это свойство или некоторая комбинация свойств, которые уникален для каждого экземпляра с одинаковым идентификатором базы данных. По сути, это естественный ключ, который вы использовали бы, если бы вы не использовали вместо него суррогатный первичный ключ. В отличие от натурального первичного ключа, это не абсолютное требование, что бизнес ключ никогда не изменяется - пока он меняется редко, этого достаточно.

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

Равенство бизнес-ключа означает, что метод equals() сравнивает только свойства, которые образуют бизнес-ключ. Это идеальное решение, которое позволяет избежать всех проблем, описанных ранее. Единственным недостатком является то, что для в первую очередь определить правильный бизнес-ключ. В любом случае, это необходимо; важно идентифицировать любые уникальные ключи, если ваша база данных должна обеспечить целостность данных с помощью проверки ограничений.

Для класса User username - отличный бизнес-ключ кандидата. Его никогда не ноль, его уникальный с ограничением базы данных, и он изменяется редко, если когда-либо:

    public class User {
        ...
        public boolean equals(Object other) {
            if (this==other) return true;
            if ( !(other instanceof User) ) return false;
            final User that = (User) other;
            return this.username.equals( that.getUsername() );
        }
        public int hashCode() {
            return username.hashCode();
        }
}

Возможно, я что-то пропустил, но для адреса, бизнес-ключ, как правило, был сделан из номера улицы, улицы, города, почтового индекса, страны. Я не вижу никаких проблем с этим.

На всякий случай, Equals And HashCode - еще одно интересное чтение.

Ответ 2

Может быть, свойство transient сделает это? Таким образом, вам не нужно беспокоиться о настойчивости. Вот так:

@Transient
private Integer otherId;

Ответ 3

Я использую это так: equal и hashcode используют ключ, когда он был установлен, иначе equals использует базовую реализацию (aka ==). Он должен работать, если hashcode() возвращает super.hashcode() вместо 0.

@Override
public int hashCode() {
    if (code == null) {
        return 0;
    } else {
        return code.hashCode();
    }
}

@Override
public boolean equals(Object obj) {
    if (obj instanceof PersistentObject && Hibernate.getClass(obj).equals(Hibernate.getClass(this))) {
        PersistentObject po = (PersistentObject) obj;

        if (code == null) {
            return po.code == null && this == po;
        } else {
            return code.equals(po.getCode());
        }
    } else {
        return super.equals(obj);
    }
}

Ответ 4

Вопрос в том, как часто вы, вероятно, имеете несколько несохраненных объектов, которые могут быть дублирующими, которые должны войти в набор или карту? Для меня ответ практически никогда не используется, поэтому я использую суррогатные ключи и super.equals/hashcode для несохраненных объектов.

Бизнес-ключи имеют смысл в некоторых случаях, но могут вызвать проблемы. Например, что, если два человека живут по одному адресу - если вы хотите, чтобы это была одна запись в базе данных, тогда вам нужно управлять им как много-ко-многим и потерять возможность каскадного удаления, так что, когда последний человек, живущий там, удален, вам нужно сделать дополнительную работу, чтобы избавиться от адреса. Но если вы сохраняете одни и те же адреса для каждого человека, тогда ваш бизнес-ключ должен включать в себя объект человека, что может означать, что база данных попала внутрь ваших методов equals/hashcode.

Ответ 5

Спасибо за весь ваш вклад. Я решил использовать суррогатные ключи и обеспечить их право на время создания объекта. Таким образом, я избегаю всего, что "редко" меняет материал и имеет что-то твердое, чтобы основать его. Первые тесты выглядят довольно хорошо.

Благодарю всех вас за ваше время. К сожалению, я могу принять только один ответ в качестве решения, я возьму Pascals, поскольку он дал мне хорошее чтение;)

пользоваться