Ошибка при сохранении новых данных с использованием существующих и несуществующих данных в базе данных jpa - PullRequest
0 голосов
/ 02 июля 2019

У меня есть Book таблица и Author таблица.Их отношения many-to-many.

Моя цель

Я хочу сохранить новую книгу.Если автор книги уже существует в таблице Author, я не хочу сохранять этого автора в таблице Author.Если автора нет в таблице Author, данные об авторе будут сохранены в таблице Author.Вот как я объявляю сущность и serviceImplementation для сохранения данных:

Книга

@Entity(name = "book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "title", nullable = false)
    private String title;
    @Column(name = "year", nullable = false)
    private String year;
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
      name = "book_author",
      joinColumns = @JoinColumn(name = "book_id", referencedColumnName = "id"),
      inverseJoinColumns = @JoinColumn(name = "author_id", referencedColumnName = "id"))
    @JsonManagedReference
    private Set<Author> author;
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", referencedColumnName = "id", nullable = false)
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
    private BookCategory category;
}

Автор

@Entity(name = "author")
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "name", nullable = false)
    private String name;
    @Column(name = "address", nullable = false)
    private String address;
    @ManyToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JsonBackReference
    private Set<Book> book;
}

BookServiceImpl

public class BookServiceImpl implements BookService {
    @Override
    public BookDto save(BookDto bookDto) throws Exception {
        try {
            Book book = new Book();
            BeanUtils.copyProperties(bookDto, book, "author", "category");
            BookCategory bookCategory = bookCategoryDao.findByCategory(bookDto.getCategory());

            Set<Author> dataAuthor = new HashSet<Author>();
            Set<AuthorDto> dataAuthorDto = new HashSet<AuthorDto>();
            bookDto.getAuthor().iterator().forEachRemaining(dataAuthorDto::add);

            for (AuthorDto authorDto : dataAuthorDto) {
                Author author = new Author();
                Author author_ = authorDao.findByName(authorDto.getName());
                if (null == author_) {
                    BeanUtils.copyProperties(authorDto, author);
                } else {
                    BeanUtils.copyProperties(author_, author);
                }

                dataAuthor.add(author);
            }

            book.setAuthor(dataAuthor);
            book.setCategory(bookCategory);
            bookDao.save(book);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e);
        }
        return null;
    }    
}

Вот пример ввода json, когда я делаю запрос:

{
    "title": "book1",
    "year": "2013",
    "author": [
        {
            "name": "angel",
            "address": "NY"
        }, {
            "name": "john",
            "address": "LA"
        }
    ],
    "category": "science"
}

Над кодом работает, если все авторы либосуществует в Author таблице или не существует вообще в Author таблице.Если один из них существует, а другой не существует, он выдаст ошибку, основанную на CascadeType, который я использовал при определении сущности.

Ошибка

Использование useВ случае «один автор существует в таблице Автор, а другой не существует в таблице Автор», здесь приведена ошибка, основанная на CascadeType:

  1. Если я использую CascadeType.all => org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persistЭто потому, что сохранение автора имеет успех только в том случае, если у него нет идентификатора, потому что идентификатор автора помечается @GeneratedValue(strategy = GenerationType.IDENTITY).
  2. Если я удаляю CascadeType или использую CascadeType.MERGE => org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing, чтопотому что у меня есть коллекция, в которой есть один или несколько элементов, которых нет в базе данных (Author таблица).

Итак, как правильно достичь этого?

1 Ответ

0 голосов
/ 02 июля 2019

Я бы попробовал это так:

for (AuthorDto authorDto : dataAuthorDto) {
    Author author = authorDao.findByName(authorDto.getName());
    if (null == author) {
        author = new Author();
        BeanUtils.copyProperties(authorDto, author);
    }
    dataAuthor.add(author);
}

Таким образом, если вы находите автора в базе данных, вы ссылаетесь на существующего автора своей книгой, а не создаете новую.Таким образом, вы не получите двух идентичных авторов в контексте постоянства (тот, который вы нашли с помощью findByName, и тот, кого вы создали и на который ссылается ваша книга).Я не тестировал это решение, пожалуйста, дайте мне знать в комментариях, если оно не работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...