首页 > 解决方案 > 使用数据库 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 的情况下才成功,因为作者的 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,这是因为我有一个集合,其中包含一个或多个数据库(Author表)中不存在的项目。

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

标签: javaspringspring-bootjpaspring-data-jpa

解决方案


我会这样尝试:

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

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


推荐阅读