Поиск по двум уникальным полям spring boot на уникальность

Я создал дочернюю сущность:

public class ChildEntity  {
   private Long bookNo;
   private String bookType;
}

Эти два поля вместе уникальны. Какие-то данные:

| No | Type      |
|----|-----------|
| 1  | Classical |
| 2  | Classical |
| 2  | Scifi     |
| 3  | Scifi     |
| 1  | Classical | (Error for uniqueness

Я создал эту сущность для поиска внутри родительской сущности.

public class ParentEntity{
    @ManyToOne
    private ChildEntity child;    
    //and other fields here
}

Кроме того, есть 2 разных родительских объекта. Они должны быть разными объектами, у них есть несколько разных полей. Но эта дочерняя сущность является общей. Итак, я буду использовать эти дочерние объекты для сравнения/различия между таблицами данных, потому что в этих дочерних объектах есть только уникальные области. Я могу получить разницу в таблицах данных через эти дочерние поля сущности.

Я создал ManyToOne, потому что у разных родителей может быть один и тот же ребенок.

Я создаю родительские объекты в сервисе следующим образом:

private createParentEntity(Dto dto){
    
    ParentEntity parent = new ParentEntity();
    //set other fields from dto
    parent.setAge(dto.getAge);
    
    ChildEntity child = new ChildEntity();
    child.setBookNo(dto.getBookNo());
    child.setBookType(dto.getBookType());
    
    parent.setChild(child);//here is problem , please look down
}

В чем моя проблема, строку я поставил комментарий.

Могут появиться одни и те же дочерние объекты, поэтому, когда я сохраняю родителей, он попытается снова сохранить дочерний элемент и может получить уникальную ошибку ограничения? Что я могу сделать для этого?

Потому что, в конце концов, я буду использовать данные Spring для поиска внутри родительского репозитория:

parentEntityRepository.findByChild(ChildEntity child);
@UniqueConstraints = {
    @UniqueConstraint(
         name = "uq_general_checklist_ordinal",
         columnNames = {"book_no", "book_type"}
    )
}
public class ChildEntity {
   @Id
   private Long bookNo;
    
   @Id
   private String bookType;
}

Это хорошее решение для размещения обоих идентификаторов? Или здесь я должен сначала сохранить ребенка и проверить, существует он или нет?

ChildEntity child =childRepository.findByBookNoAndBookType(
     dto.getBookNo(), 
     dto.getBookType()
);//if exists, set to parent. if not, create new and set it?

person msadasjwd    schedule 26.06.2020    source источник


Ответы (2)


Я бы сделал что-то вроде этого: Или здесь я должен сначала сохранить дочерний элемент и проверить, существует он или нет?

Однако вместо того, чтобы сначала сохранять дочерний элемент, просто найдите его с новыми идентификаторами. Таким образом, вы избегаете исключений SQL. Если его нет, все равно не спасайте ребенка. Добавьте его к родителю и сохраните родителя. Следовательно, это спасет ребенка. Если он существует, добавьте его к новому родителю и сохраните родителя.

person Eliaquim Tchitalacumbi    schedule 26.06.2020
comment
Итак, для дочернего объекта вы предлагаете указать поле 3. как столбец идентификатора? - person msadasjwd; 26.06.2020
comment
Я не знаю, понимаю ли я ваш вопрос, но я понял, что то, что вы называете ChildEntity, может иметь более одного родителя. Если вы сейчас спрашиваете о размещении идентификатора ChildEntity в Parent, да, это будет работать с тем же подходом, что и в моем ответе. Однако, даже если вы просто добавите сам ChildEntity, Hibernate просто будет использовать столбец ID в Parent. - person Eliaquim Tchitalacumbi; 27.06.2020
comment
Теперь у меня есть две колонки в дочернем: bookno, booktype. Вы предлагаете иметь идентификатор в качестве 3. столбца? - person msadasjwd; 27.06.2020
comment
Нет! Поскольку у родителей может быть один и тот же дочерний элемент, на него следует ссылаться в Parent. Либо по дочернему объекту, который у вас есть, либо по дочернему идентификатору. - person Eliaquim Tchitalacumbi; 29.06.2020

Если только я не хочу активно использовать кэширование первого уровня Hibernate и тому подобное, мой подход goto для плотных сценариев One-to-Many состоит в том, чтобы сделать это однонаправленным отношением, но, за исключением части загрузки, снять всю ответственность с рук Hibernate и просто сделать это сам.

Это делает его предсказуемым и экономит много времени при работе с Hibernate и возникающих проблемах с другими соглашениями, предположениями людей и другими фреймворками (например, рекурсивные методы toString, equals, hashcode, когда они автоматически сгенерированы/не тщательно обработаны).

Для этого я использую:

@OneToMany
@JoinColumn(insertable = false, updatable = false)

у родителя и просто простой столбец идентификатора у дочернего элемента.

person Frank Hopkins    schedule 26.06.2020
comment
Как я буду вставлять дочерние объекты? Потому что я буду получать родительские объекты, поэтому, возможно, новые дочерние объекты могут получать от этих родителей? - person msadasjwd; 27.06.2020
comment
@msadasjwd у вас есть отдельный дао/репозиторий для дочерних объектов, и вы вставляете их явно через него. Вы можете получить родителя с текущими дочерними элементами через parentRepository.findAllById(1), а затем определить, что вам нужно добавить дочерний элемент, потому что его еще нет, построить его со ссылкой на первичный ключ родителя - например, child5 - и вставить в базу данных через childRepository.save(child5). - person Frank Hopkins; 27.06.2020
comment
Да, отдельный репозиторий для ребенка. Итак, чтобы сравнить 2 родительских объекта, сначала я должен получить дочерний идентификатор из дочернего репозитория через эти два поля. Затем с родительским репозиторием я буду искать с этим идентификатором, например parentrepository.findAllByChildEntityId(Long id) - person msadasjwd; 27.06.2020