错误使用数据库jpa中的现有和不存在的数据来持久存储新数据

时间:2019-07-02 15:46:58

标签: java spring spring-boot jpa spring-data-jpa

我有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引发错误。

错误

使用用例“作者表中存在一个作者,而作者表中不存在另一个作者”,这是基于CascadeType的错误:

  1. 如果我使用CascadeType.all => org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist,这是因为持久保留作者只有在没有id的情况下才是成功的,因为author的ID用@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,这是因为我有一个包含一个或多个数据库中不存在的项目的集合({{1} }表)。

那么,实现它的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

我会这样尝试:

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

这样,如果您在数据库中找到一位作者,则将现有的作者与您的书一起引用,而不用创建新的作者。这样,您就不会在持久性上下文中得到两个相同的作者(一个是使用findByName找到的,另一个是您创建并被您的书引用的)。我没有测试此解决方案,如果无法解决,请在评论中告知我。