#java #hibernate #jpa #spring-data-jpa #lombok
Вопрос:
Я пытаюсь вставить список сущностей, которые имеют взаимное отношение к другой сущности. Возможно, что сопоставленный объект «один к одному» будет одинаковым для многих родительских объектов. Я ожидаю, что одна и та же дочерняя сущность упоминается во внешних ключах родителя, но на самом деле создаются повторяющиеся строки. Вот мои Сущности.
@Builder
@Entity
public class PaymentInfoType1 {
@Id
Long id;
LocalDate date;
@Column(precision = 15, scale = 2)
BigDecimal amount;
String reference;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "account", referencedColumnName = "id")
Account account;
}
@Builder
@Entity
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Account {
@Id
Long id;
@EqualsAndHashCode.Include
String name;
@EqualsAndHashCode.Include
String accountId;
}
Я создаю список PaymentInfoType1 на основе информации, полученной из другой системы. Каждый PaymentInfoType1 создается вместе со своей учетной записью, которая может содержать точно такую же информацию, но разные объекты в реальном времени.
Когда я это сделаю:
PaymentInfoType1 first = // Created with some logic
Account account1 = // name = sample amp; accountId = 123
first.setAccount(account1);
PaymentInfoType1 second = // Created with some logic
Account account2 = // name = sample amp; accountId = 123
second.setAccount(account2);
// Both the above its own account object but the field have exactly same values.
List<PaymentInfoType1> list = List.of(first, second);
repo.saveAll(list);
Я ожидал, что в таблице PaymentInfoType1 будет две строки и одна в учетной записи, но обнаружил, что в учетной записи также есть две строки. Выглядит как Равный, и в этом случае хэш-код не оказывает никакого влияния.
Как можно справиться с этим, чтобы не вставлять повторяющиеся строки, когда объекты отображения похожи по значениям/хэш-коду.
Ответ №1:
JPA ничего не делает с @EqualsAndHashcode
(это просто генерирует методы класса equals
и hashCode
).
JPA идентифицирует сущности по идентификатору сущности, аннотированному @Id
(или @EmebeddedId
), и этот идентификатор также может быть реализован и проверен — и обычно также генерируется (как некоторая последовательность бд) — на уровне базы данных.
Если вы хотите использовать Account
идентифицированный name
и accountId
на стороне JPA, вам нужно использовать @EmbeddedId
и @Embeddable
и избавиться от @Id
него . Это было бы что-то вроде:
@Embeddable
public class AccountId {
String name;
String accountId; // maybe needs renaming...
}
а затем в Account
:
@EmbeddedId
AccountId accountId;
Смотрите это, например
Комментарии:
1. Спасибо, пирхо. Я устал, но получил исключение : другой объект с тем же значением идентификатора уже был связан с сеансом. Я предполагаю, что после первой вставки вторая вставка выходит из строя из-за ограничения первичного ключа. Могу ли я каким-то образом сохранить список и позволить hibernate выполнить слияние.
2. Что, скорее всего, является абсолютно правильной ошибкой. Это говорит вам о том, что нет смысла создавать и сохранять такую вторую сущность с теми же значениями идентификаторов в том же сеансе. Вместо этого вы должны просто использовать это в первую очередь для обеих учетных записей, в которых также нет смысла, поскольку у вас отношения один к одному.
3. Мне придется самому проверить, совпадают ли объекты, и установить один и тот же объект в родительских сущностях. Значения выбираются из другой системы, другого способа нет, кроме как я сам проверяю, совпадают ли объекты, прежде чем создавать новый.
4. Ну, в любом случае нет причин, которые мешали бы использовать также equals и хэш-код на другой стороне логики. Только вам нужно добавить это
@EqualsAndHashcode
в встраиваемый и включить только этот встраиваемый идентификатор (AccountId) в ваш ломбок@EqualsAndHashCode
в классе учетной записи