3

I have one-directional related entities:

@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;
}

And the following test:

@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) works perfectly well when this code is invoked not from test but in the test I get exception that recommendation isn't deleted by book.

Also I try to get any info from sql logged for hibernate queries and I can't see any delete statements for this test.

I don't want to use bidirectional linking for entities and just try to understand how to solve this concrete issue or debug it somehow.

Ray
  • 1,788
  • 7
  • 55
  • 92
  • Maybe should be in a transaction: https://stackoverflow.com/questions/17263475/how-to-flush-data-into-db-inside-active-spring-transaction – K.Nicholas Jun 13 '19 at 18:54

2 Answers2

2

@ManyToOne#cascade

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

EDIT 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;

The following approach should do the trick in case you don't want bi-directional mappings and your schema is created from your entites.

In case you create schema from SQL you can also create such a foreign key but in your script.

This approach also expects only one delete query to be executed.

Serg Vasylchak
  • 858
  • 4
  • 18
  • I don't want to get side effects when I remove `Recommendation` I don't want to remove book as well – Ray Jun 13 '19 at 20:32
  • Oh, indeed, didn't look thoroughly into the problem. In this case I am not sure if there is any other ways except of: `@OneToMany(mappedBy="book", orphanRemoval=true)` relation in your `Book` entity. But I don't like bidirectional relations either, so in such cases I use `FOREIGN KEY ON DELETE CASCADE` in SQL table definition (Recommendation table with book_id foreign key in your case). – Serg Vasylchak Jun 13 '19 at 21:25
2

A couple of points to note:

Firstly, as far as I am aware @OnDelete(action = OnDeleteAction.CASCADE) only affects DDL generation i.e. the FK is created in the database with the on delete cascade clause. You are then delegating the cacading delete to the database and if you are not having Hibernate generate the schema then this annotation will have no effect. As the cascading delete is delegated to the database you will not then see any SQL delete statements being executed by Hibernate.

Secondly, in your test, you are deleting the entity using a JPQL query:

bookRepository.deleteBookByIsbn(book.getIsbn());

rather than by using entityManager.remove(e) (via your repository's delete(e) method).

The result of this is that even if you were to apply the JPA CacadeType.DELETE to the association the delete operation would not cascade as bulk JPQL update/delete queries do not cacade.

See further on this point here:

JPA delete all entites works strange

Alan Hay
  • 22,665
  • 4
  • 56
  • 110