Использование Spring Data JPA - как сохранить ТОЛЬКО УНИКАЛЬНЫЕ элементы в дочерней сущности при сохранении родительской сущности и с помощью CascadeType=PERSIST
Я готовлю простое приложение Spring. У меня есть 2 объекта: Book.class (родительский) и Author.class (дочерний): с @OneToMany из представления "Автор" и @ManyToOne(cascade=CascadeType.PERSIST) из отношений представления "Книга". При сохранении новой книги - также автор сохраняется и добавляется в БД ( mySql)- что я и хочу. Но я не могу понять, почему Spring добавляет Author - если такой элемент уже существует. Как изменить код, чтобы убедиться, что в БД будут добавлены только уникальные Авторы, и в таблице авторов в БД не будет дубликатов?
Я добавил hashCode и equals методы в класс Author, но это не помогло. Я пытался изменить также Cascade.Type, но также не помогло.
Author.class (часть кода):
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
@OneToMany(mappedBy = "author")
@JsonManagedReference(value = "book-author")
private Set<Book> books = new HashSet<Book>();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Author author = (Author) o;
return Objects.equals(getFirstName(), author.getFirstName()) &&
Objects.equals(getLastName(), author.getLastName());
}
@Override
public int hashCode() {
return Objects.hash(getFirstName(), getLastName());
}
И Book.class (часть кода):
@Entity
public class Book {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToOne( cascade=CascadeType.PERSIST)
@JoinColumn(name = "author_id", unique = true)
@JsonBackReference(value="book-author")
private Author author;
Редактировать 1 BookServiceImpl.class
@Override
public BookDto addBookDto(BookDto bookDto) {
Book book = bookConverter.apply(bookDto);
bookRepository.save(book);
return bookDtoConverter.apply(book);
}
AuthorServiceImpl.class
@Override
public Author findAuthor(String firstName, String lastName) {
Optional<Author> authorByNameOptional = authorRepository.findByFirstNameAndLastName(firstName, lastName);
if (authorByNameOptional.isPresent()) {
return authorByNameOptional.get();
} else {
Author newAuthor = new Author();
newAuthor.setFirstName(firstName);
newAuthor.setLastName(lastName);
return newAuthor;
}
И BookWebController.class
@PostMapping("/addWebBook")
public String addBook(@ModelAttribute(name = "addedBook") BookDto addedBook, Model model) {
Author author1 = addedBook.getAuthor();
Author author = authorService.findAuthor(author1.getFirstName(), author1.getLastName());
addedBook.setAuthor(author);
bookService.addBookDto(addedBook);
return "redirect:/message?msg";
}
Был бы признателен за любую подсказку, так как я совсем новичок в этой области:-)
1 ответ
- Позвольте мне предположить, что ваш
Author
объект вBook
имеет пустое поле первичного ключа? Логика Hibernate в этом случае: пустой идентификатор означает новую строку для вставки. Это может работать правильно, если вы установите первичный ключ для автора. Например, пользователь может найти автора (с его PK) или добавить новый (без PK) и вызвать метод save(book), который будет каскадно сохранять только нового автора. В большинстве случаев это обычно работает так. Еще один момент, на который следует обратить внимание: если вы хотите сохранить уникальность автора в базе данных, вы должны наложить ограничения на сущность автора. Например, если у каждого автора должно быть уникальное имя и фамилия, это может выглядеть примерно так:
@Entity @Table(uniqueConstraints= @UniqueConstraint(columnNames={"first_name", "last_name"})) public class Author { ...
После этого DataIntegrityViolationException будет генерироваться при вставке дублирующего значения, и ваша база данных будет оставаться чистой от дубликатов.