【问题标题】:Avoid add same data with many-to-many relationship jpa避免使用多对多关系 jpa 添加相同的数据
【发布时间】:2019-07-02 10:46:37
【问题描述】:

我有一个场景,一本书可以有一个类别并且属于多个作者。一个类别可以有很多书。作者可能属于很多书籍。

所以,

  1. BookBookCategorymany-to-one
  2. BookAuthorAuthorBookmany-to-many(在 book_author 表中)
  3. BookCategoryBookone-to-many

我可以与作者和类别一起保存(使用以下代码)新书。但是,问题是:

  1. 如果我没有检查作者是否已经存在于Author表中,同一作者将再次插入Author表中
  2. 如果我检查Author表中是否已经存在同一作者,然后从列表中省略作者,book_author表如何知道这本书是否与已经存在的作者有关系?

以下是我声明实体和服务的方式:

BookDto

public class BookDto {
    private String title;
    private String year;
    private Set<AuthorDto> author;
    private String category;
}

AuthorDto

public class AuthorDto {
    private String name;
    private String address;
}

图书

@Entity(name = "book")
@Table(name = "book", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
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")
@Table(name = "author", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
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;
}

图书分类

@Entity(name = "book_category")
@Table(name = "book_category")
public class BookCategory {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "category", nullable = false)
    private String category;
}

BookServiceImpl

public class BookServiceImpl implements BookService {
    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();
                /** 
                 * Problem is:
                 * 1. If I did not check whether the author already exist in
                 * Author table, the same author will inserted again into Author table
                 * 2. If I check whether same author already exist in Author table, then omit
                 * the author from the list, how book_author table would know if
                 * the book have relationship with already existed author?
                 * */
                BeanUtils.copyProperties(authorDto, author);
                dataAuthor.add(author);
            }

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

任何帮助都会非常有帮助。谢谢!

【问题讨论】:

  • 如果实体与现有实体有关系,那么您从数据库中获取该实体并将其设置为关系的目标。
  • @AlanHay 您能否根据我迄今为止所做的工作给我关于“将其设置为关系的目标”的伪代码?
  • ????如果实体在数据库中,则加载它并将其添加到集合中。 book.getAuthors().add(someExistingAuthorEntity);

标签: java spring-boot jpa


【解决方案1】:

如果我没有检查作者是否已经存在于Author表中,同一作者将再次插入到Author表中

正确。由此得出的结论是,如果您不想重复,您应该检查作者是否已经存在,无论您对“同一”作者的业务定义是什么。

如果我检查Author表中是否已经存在相同的作者,然后从列表中省略作者,book_author表如何知道这本书是否与已经存在的作者有关系?

好吧,那就不要省略它。而是查一下。假设您对同一作者的定义是“同名作者”,只需将以下方法添加到DataAuthorRepository(或您所称的任何名称):

Optional<Author> findByNameIgnoreCase(String name)

然后,在BookServiceImpl 中,只需执行以下操作:

for (AuthorDto authorDto : dataAuthorDto) {
    Author author = dataAuthor.findByNameIgnoreCase(authorDto.getName())
    .orElseGet(() -> {
        Author newAuthor = new Author();
        BeanUtils.copyProperties(authorDto, newAuthor);
        return newAuthor;
    });
    dataAuthor.add(author);
} 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-22
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 2023-03-21
    • 2019-01-06
    • 2011-09-27
    • 2013-04-08
    相关资源
    最近更新 更多