Using Spring Data JPA - how to save ONLY UNIQUE items in child entity while saving parent entity and using CascadeType=PERSIST

时间:2019-01-18 18:51:42

标签: spring spring-data-jpa hibernate-cascade

I am preparing simple Spring app. I have 2 entities : Book.class (parent) and Author.class (child): with @OneToMany from Author view and @ManyToOne(cascade=CascadeType.PERSIST) from Book view relations. While saving new Book - also Author is being saved and added to DB( mySql)- which is what I want. But I cannot understand why Spring adds Author - if such item already exists. How to change the code to make sure that only unique Authors will be added to DB and there will be no duplicates in author table in DB?

I've added hashCode and equals methods to Author class but it did not help. I've tried to change also Cascade.Type but also did not help.

The Author.class(part of code):

@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());
    }

And the Book.class(part of code):

@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;

Edit 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;
    }

And 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";
    }

Would be greatful for any hint as I am quite new to this area :-)

1 个答案:

答案 0 :(得分:0)

  1. 让我建议您在Author中的Book对象具有空的主键字段?在这种情况下,Hibernate的逻辑是:id为空意味着要插入新行。如果您设置作者的主键,它可能会正常工作。例如,用户可以找到作者(带有PK)或添加新作者(不带PK),然后调用save(book)方法,该方法将级联地仅保留新作者。在大多数情况下,通常都是这样。
  2. 另一个需要注意的时刻,如果要在数据库中保留作者的唯一性,则必须对作者的实体施加约束。 例如,如果每个作者必须具有唯一的名字和姓氏,则它可能看起来像这样:

    @Entity
    @Table(uniqueConstraints= @UniqueConstraint(columnNames={"first_name", "last_name"}))
    public class Author {
    ...
    

    此后,DataIntegrityViolationException将在重复值插入时抛出,并且您的数据库将保持重复。