hibernate spring jpa多对多只删除关联

dfty9e19  于 2023-04-30  发布在  Spring
关注(0)|答案(1)|浏览(151)

我深入研究了如何只删除多对多的关联而不删除任何一个实体,我找到的所有教程或示例都是关于如何删除一个实体而不级联删除其他实体或整个表(当使用CascadeType时)。ALL)。
所以:我有两个表和一个多对多表,保存它们之间的关系。

authors( int author_id, varchar author_name )
books( int book_id, varchar book_name )
author_x_book( int author_id, int book_id )

一个作者可以有很多书,一本书可以有很多作者。我不进入关于主键和外键的细节,以及为什么我选择了这种设计等。
JPA实体:

@Entity
@Table(name = "authors")
@Data
public class Author implements Serializable {
    @Id
    @Column(name = "author_id", nullable = false)
    private Long id;

    @Column(name = "author_name")
    private String name;

    @ManyToMany(fetch = FetchType.LAZY,
        cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
    @JoinTable(name = "author_x_book",
        joinColumns = @JoinColumn(name = "author_id", referencedColumnName = "author_id"),
        inverseJoinColumns = @JoinColumn(name = "book_id", referencedColumnName = "book_id"))
    private List<Book> books;
}

@Entity
@Table(name = "books")
@Data
public class Book implements Serializable {
    @Id
    @Column(name = "book_id", nullable = false)
    private Long id;

    @Column(name = "book_name")
    private String name;

    @ManyToMany(mappedBy = "books", fetch = FetchType.EAGER,
        cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
    private List<Author> authors;
}

我试图只删除author_x_book中的条目-也就是说,删除特定书籍和特定作者之间的关联。我不打算删除作者和这本书,因为这本书可能有其他作者,作者可能有其他书。
我试图把作者从书中删除。作者和作者的书。于是,书既保留了书,也保留了作者。

@Transactional
public void deleteAssociation(String authorName, String bookName)
{
    Author author = authorRepository.findFirstByName(authorName);
    Book book = bookRepository.findFirstByName(bookName);
    deleteAssociation(author, book);
}

private void deleteAssociation(Author author, Book book) {
    book.getAuthors().remove(author);
    author.getBooks().remove(book);
    authorRepository.save(author);
    bookRepository.save(book);
}

不工作。它试图删除所有作者的联系,他所有的书。然后,它尝试将该关联添加回来。Sql日志:

delete 
from
    "public"."author_x_book" 
where
    "author_id"=?

insert 
into
    "public"."author_x_book" ("author_id", "book_id") 
values
    (?, ?)

然后,我创建了一个AuthorXBook实体和@EmbeddedId,并创建了一个AuthorXBookRepository。

@Data
@Entity
@Table(name = "author_x_book")
public class AuthorXBook implements Serializable {
    @EmbeddedId
    private AuthorXBookId id;

    @Data
    @Embeddable
    public static class AuthorXBookId implements Serializable {
        @Column(name = "author_id", nullable = false)
        private final long authorId;
        @Column(name = "book_id", nullable = false)
        private final long bookId;
    }
}

@Repository
public interface AuthorXBookRepository extends CrudRepository<AuthorXBook, AuthorXBook.AuthorXBookId> {
}

调用AuthorXBookRepository时。delete(authorXBook):

private void deleteAssociation(Author author, Book book) {
    authorXBookRepository.delete(new AuthorXBook.AuthorXBookId(author.getId(), book.getId()));
    book.getAuthors().remove(author);
    author.getBooks().remove(book);
    authorRepository.save(author);
    bookRepository.save(book);
}

它做了一个选择,而不是删除,然后删除作者的所有书籍并添加回关联:

select
    authorxboo0_."author_id" as author_1_20_0_,
    authorxboo0_."book_id" as book_2_20_0_ 
from
    "public"."author_x_book" authorxboo0_ 
where
    authorxboo0_."author_id"=? 
    and authorxboo0_."book_id"=?

delete 
from
    "public"."author_x_book" 
where
    "author_id"=?

insert 
into
    "public"."author_x_book" ("author_id", "book_id") 
values
    (?, ?)

我最终创建了一个@Modifying @Query来删除关联:

@Repository
public interface AuthorXBookRepository extends CrudRepository<AuthorXBook, AuthorXBook.AuthorXBookId> {
    @Modifying
    @Query("delete from #{#entityName} x where x.authorId=:aid and x.bookId=:bid")
    void delete(@Param("aid") long authorId, @Param("bid") long bookId);
}

private void deleteAssociation(Author author, Book book) {
    authorXBookRepository.delete(author.getId(), book.getId());
    book.getAuthors().remove(author);
    author.getBooks().remove(book);
    authorRepository.save(author);
    bookRepository.save(book);
}

而且成功了它只删除关联。并且不尝试将其添加回来,我猜是因为@Modifiable清除了EntityManager。
所以,两个问题:
1.为什么我的第一次和第二次尝试都没有成功?
1.只删除多对多关联而不删除任何实体的正确方法是什么?
谢谢你

ehxuflar

ehxuflar1#

在所有者端使用“设置”而不是“列表”,以避免“删除所有内容,然后再次插入剩余内容”的问题:

@Entity
@Table(name = "authors")
@Data
public class Author implements Serializable {
    @Id
    @Column(name = "author_id", nullable = false)
    private Long id;

    @Column(name = "author_name")
    private String name;

    @ManyToMany(fetch = FetchType.LAZY,
        cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
    private Set<Book> books;  // use Set instead of List on Owner Side
}

当只删除关系时,在删除彼此的引用后保存Owner“Author”即可,

@Transactional
public void deleteAssociation(String authorName, String bookName)
{
    Author author = authorRepository.findFirstByName(authorName);
    Book book = bookRepository.findFirstByName(bookName);
    deleteAssociation(author, book);
}

private void deleteAssociation(Author author, Book book) {
    book.getAuthors().remove(author);
    author.getBooks().remove(book);
    authorRepository.save(author);  // saving Owner is enought
    // bookRepository.save(book);
}

Entity Author和Book需要分别覆盖这两个函数

@Override 
public boolean equals(Object o) {
   // compare by their Id
}
@Override 
public int hashCode() {
    return Objects.hashCode(getClass());
}

相关问题