Хранилище Spring сохраняет все вставки повторяющихся строк для сопоставленной сущности

#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 в классе учетной записи