Spring Data JPA Delete and Relationships

1. Overview

In this tutorial, we’ll have a look at how deleting is done in Spring Data JPA.

2. Sample Entity

As we know from the Spring Data JPA reference documentation, repository interfaces provide us some basic support for entities.

If we have an entity, like a Book:

@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;
    private String title;

    // standard constructors

    // standard getters and setters
}

Then, we can extend Spring Data JPA’s CrudRepository to give us access to CRUD operations on Book:

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {}

3. Delete from Repository

Among others, CrudRepository contains two methods: deleteById and deleteAll.

Let’s test these methods directly from our BookRepository:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class DeleteFromRepositoryUnitTest {

    @Autowired
    private BookRepository repository;

    Book book1;
    Book book2;
    List<Book> books;

    // data initialization

    @Test
    public void whenDeleteByIdFromRepository_thenDeletingShouldBeSuccessful() {
        repository.deleteById(book1.getId());
        assertThat(repository.count()).isEqualTo(1);
    }

    @Test
    public void whenDeleteAllFromRepository_thenRepositoryShouldBeEmpty() {
        repository.deleteAll();
        assertThat(repository.count()).isEqualTo(0);
    }
}

And even though we are using CrudRepository, note that these same methods exist for other Spring Data JPA interfaces like JpaRepository or PagingAndSortingRepository.

4. Derived Delete Query

We can also derive query methods for deleting entities. There is a set of rules for writing them, but let’s just focus on the simplest example.

A derived delete query must start with deleteBy, followed by the name of the selection criteria. These criteria must be provided in the method call.

Let’s say that we want to delete Books by title. Using the naming convention, we’d start with deleteBy and list title as our criteria:

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
    long deleteByTitle(String title);
}

The return value, of type long, indicates how many records the method deleted.

Let’s write a test and make sure that is correct:

@Test
@Transactional
public void whenDeleteFromDerivedQuery_thenDeletingShouldBeSuccessful() {
    long deletedRecords = repository.deleteByTitle("The Hobbit");
    assertThat(deletedRecords).isEqualTo(1);
}

Persisting and deleting objects in JPA requires a transaction, that’s why we should use a @Transactional annotation when using these derived delete queries, to make sure a transaction is running. This is explained in detail in the ORM with Spring documentation.

5. Custom Delete Query

The method names for derived queries can get quite long, and they are limited to just a single table.

When we need something more complex, we can write a custom query using @Query and @Modifying together.

Let’s check the equivalent code for our derived method from earlier:

@Modifying
@Query("delete from Book b where b.title=:title")
void deleteBooks(@Param("title") String title);

Again, we can verify it works with a simple test:

@Test
@Transactional
public void whenDeleteFromCustomQuery_thenDeletingShouldBeSuccessful() {
    repository.deleteBooks("The Hobbit");
    assertThat(repository.count()).isEqualTo(1);
}

Both solutions presented above are similar and achieve the same result. However, they take a slightly different approach.

The @Query method creates a single JPQL query against the database. By comparison, the deleteBy methods execute a read query, then delete each of the items one by one.

6. Delete in Relationships

Let’s see now what happens when we have relationships with other entities.

Assume we have a Category entity, that has a OneToMany association with the Book entity:

@Entity
public class Category {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Book> books;

    // standard constructors

    // standard getters and setters
}

The CategoryRepository can just be an empty interface that extends CrudRepository:

@Repository
public interface CategoryRepository extends CrudRepository<Category, Long> {}

We should also modify the Book entity to reflect this association:

@ManyToOne
private Category category;

Let’s now add two categories and associate them with the books we currently have. Now, if we try to delete the categories, the books will also be deleted:

@Test
public void whenDeletingCategories_thenBooksShouldAlsoBeDeleted() {
    categoryRepository.deleteAll();
    assertThat(bookRepository.count()).isEqualTo(0);
    assertThat(categoryRepository.count()).isEqualTo(0);
}

This is not bi-directional, though. That means that if we delete the books, the categories are still there:

@Test
public void whenDeletingBooks_thenCategoriesShouldAlsoBeDeleted() {
    bookRepository.deleteAll();
    assertThat(bookRepository.count()).isEqualTo(0);
    assertThat(categoryRepository.count()).isEqualTo(2);
}

We can change this behavior by changing the properties of the relationship, such as the CascadeType.

7. Conclusion

In this article, we looked at different ways to delete entities in Spring Data JPA. We looked at the provided delete methods from CrudRepository, as well as our derived queries or custom ones using @Query annotation.

We also had a look at how deleting is done in relationships. As always, all of the code snippets mentioned in this article can be found on our GitHub repository.

Related posts:

Java Program to Find SSSP (Single Source Shortest Path) in DAG (Directed Acyclic Graphs)
Entity To DTO Conversion for a Spring REST API
Guide to BufferedReader
Using the Map.Entry Java Class
Spring WebClient Filters
Spring Boot Configuration with Jasypt
Java Program to Implement Graham Scan Algorithm to Find the Convex Hull
Java Program to Implement CopyOnWriteArraySet API
Giới thiệu Swagger – Công cụ document cho RESTfull APIs
Java Program to Implement Depth-limited Search
Anonymous Classes in Java
Check if there is mail waiting
Java Program to Permute All Letters of an Input String
Lớp HashMap trong Java
Java Program to Implement Tarjan Algorithm
Spring Boot: Customize Whitelabel Error Page
Working with Network Interfaces in Java
Java Program to Represent Graph Using 2D Arrays
Spring Cloud – Tracing Services with Zipkin
The “final” Keyword in Java
Mảng (Array) trong Java
Java Program to Implement LinkedBlockingDeque API
Netflix Archaius with Various Database Configurations
Shuffling Collections In Java
Hướng dẫn Java Design Pattern – Memento
Java Program to Describe the Representation of Graph using Incidence Matrix
Giới thiệu Design Patterns
Using a Custom Spring MVC’s Handler Interceptor to Manage Sessions
Thực thi nhiều tác vụ cùng lúc như thế nào trong Java?
Inheritance and Composition (Is-a vs Has-a relationship) in Java
The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5
Java Program to Find Minimum Number of Edges to Cut to make the Graph Disconnected