【问题标题】:Hibernate OnDelete cascade in test doesn't work测试中的休眠 OnDelete 级联不起作用
【发布时间】:2019-10-28 05:41:54
【问题描述】:

我有单向相关实体:

@Entity
public class Book {
    private String isbn;
}

@Entity
private class Recommentation {
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name = "book_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Book book;
}

还有以下测试:

@RunWith(SpringRunner.class)
@DataJpaTest
public class BookRepositoryTest {

    @Autowired
    private TestEntityManager testEntityManager;

    @Autowired
    private BookRepository bookRepository;

    @Test
    public void delete() {
        // given
        String isbn = "isbn-1";
        Book book = new Book();
        book.setIsbn(isbn);
        testEntityManager.persist(book);


        Recommendation recommendation = new Recommendation();
        recommendation.setBook(book);
        testEntityManager.persist(recommendation);

        // when
        bookRepository.deleteBookByIsbn(book.getIsbn());

        // then
        assertThat(testEntityManager.find(Book.class, book.getId())).isNull();
        assertThat(testEntityManager.find(Recommendation.class, recommendation.getId())).isNull();
    }

}

@OnDelete(action = OnDeleteAction.CASCADE) 在不是从测试中调用此代码时效果很好,但在测试中我得到了建议没有被书删除的异常。

我还尝试从为休眠查询记录的 sql 中获取任何信息,但我看不到任何用于此测试的 delete 语句。

我不想对实体使用双向链接,只是尝试了解如何解决这个具体问题或以某种方式对其进行调试。

【问题讨论】:

标签: hibernate spring-boot jpa spring-data-jpa spring-test


【解决方案1】:

@ManyToOne#cascade

@ManyToOne(optional = false, cascade = CascadeType.ALL)
@JoinColumn(name = "book_id", nullable = false)
private Book book;

编辑 1:

@ManyToOne(optional = false)
@JoinColumn(name = "book_id",
        nullable = false,
        foreignKey = @ForeignKey(
                foreignKeyDefinition = "FOREIGN KEY (book_id) REFERENCES book(id) ON DELETE CASCADE"
        )
)
private Book book;

如果您不想要双向映射并且您的架构是从您的实体创建的,则以下方法应该可以解决问题。

如果您从 SQL 创建架构,您也可以在脚本中创建这样的外键。

这种方法还期望只执行一个删除查询。

【讨论】:

  • 我不想在删除Recommendation时产生副作用我也不想删除书
  • 哦,确实,没有仔细研究这个问题。在这种情况下,我不确定是否有任何其他方式,除了:@OneToMany(mappedBy="book", orphanRemoval=true) 关系中的 Book 实体。但我也不喜欢双向关系,所以在这种情况下,我在 SQL 表定义中使用FOREIGN KEY ON DELETE CASCADE(在你的情况下是带有 book_id 外键的推荐表)。
【解决方案2】:

需要注意的几点:

首先,据我所知@OnDelete(action = OnDeleteAction.CASCADE) 仅影响 DDL 生成,即 FK 是使用 on delete cascade 子句在数据库中创建的。然后,您将级联删除委托给数据库,如果您没有让 Hibernate 生成模式,则此注释将无效。由于级联删除被委托给数据库,因此您不会看到 Hibernate 正在执行任何 SQL 删除语句。

其次,在您的测试中,您正在使用 JPQL 查询删除实体:

bookRepository.deleteBookByIsbn(book.getIsbn());

而不是使用entityManager.remove(e)(通过您的存储库的delete(e) 方法)。

这样做的结果是,即使您将 JPA CacadeType.DELETE 应用于关联删除操作也不会级联,因为批量 JPQL 更新/删除查询不会级联

在此处进一步了解这一点:

JPA delete all entites works strange

【讨论】:

    猜你喜欢
    • 2011-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    相关资源
    最近更新 更多