Spring Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Introduction

Spring Data JPA allows us to define derived methods that read, update or delete records from the database. This is very helpful as it reduces the boilerplate code from the data access layer.

In this tutorial, we’ll focus on defining and using Spring Data derived delete methods with practical code examples.

2. Derived deleteBy Methods

First, let’s set up our example. We’ll define a Fruit entity to save the name and color of items available in a fruit store:

@Entity
public class Fruit {
    @Id
    private long id;
    private String name;
    private String color;
    // standard getters and setters
}

Next, we’ll add our repository to operate on Fruit entities, by extending the JpaRepository interface and adding our derived methods to this class.

Derived methods can be defined as VERB + attribute defined in an entity. A few of the allowed verbs are findBy, deleteBy, and removeBy.

Let’s derive a method to delete Fruits by their name:

@Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {
    Long deleteByName(String name);
}

In this example, the deleteByName method returns the count of deleted records.

Similarly, we can also derive a delete method of the form:

List<Fruit> deleteByColor(String color);

Here, the deleteByColor method deletes all fruits with a given color and returns a list of deleted records.

Let’s test the derived delete methods. First, we’ll insert a few records in the Fruit table, by defining the data in test-fruit-data.sql:

insert into fruit(id,name,color) values (1,'apple','red');
insert into fruit(id,name,color) values (2,'custard apple','green');
insert into fruit(id,name,color) values (3,'mango','yellow');
insert into fruit(id,name,color) values (4,'guava','green');

Then, we’ll delete all “green” fruit:

@Transactional
@Test
@Sql(scripts = { "/test-fruit-data.sql" })
public void givenFruits_WhenDeletedByColor_ThenDeletedFruitsShouldReturn() {
     List<Fruit> fruits = fruitRepository.deleteByColor("green");

     assertEquals("number of fruits are not matching", 2, fruits.size());
     fruits.forEach(fruit -> assertEquals("It's not a green fruit", "green", fruit.getColor()));
}

Also, note that we need to use the @Transactional annotation for delete methods.

Next, let’s add a similar test case for the second deleteBy method:

@Transactional
@Test
@Sql(scripts = { "/test-fruit-data.sql" })
public void givenFruits_WhenDeletedByName_ThenDeletedFruitCountShouldReturn() {

    Long deletedFruitCount = fruitRepository.deleteByName("apple");

    assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
}

3. Derived removeBy Methods

We can also use the removeBy verb to derive delete methods:

Long removeByName(String name);
List<Fruit> removeByColor(String color);

Note that there’s no difference in the behavior of the two types of methods.

The final interface will look like:

@Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {

    Long deleteByName(String name);

    List<Fruit> deleteByColor(String color);

    Long removeByName(String name);

    List<Fruit> removeByColor(String color);
}

Let’s add similar unit tests for the removeBy methods:

@Transactional
@Test
@Sql(scripts = { "/test-fruit-data.sql" })
public void givenFruits_WhenRemovedByColor_ThenDeletedFruitsShouldReturn() {
    List<Fruit> fruits = fruitRepository.removeByColor("green");

    assertEquals("number of fruits are not matching", 2, fruits.size());
}
@Transactional
@Test
@Sql(scripts = { "/test-fruit-data.sql" })
public void givenFruits_WhenRemovedByName_ThenDeletedFruitCountShouldReturn() {
    Long deletedFruitCount = fruitRepository.removeByName("apple");

    assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
}

4. Derived Deleted Methods vs @Query

We may come across a scenario that makes the derived method’s name too big or that involves a SQL JOIN between unrelated entities.

In this case, we can also use the @Query and @Modifying annotations to implement delete operations.

Let’s see the equivalent code for our derived delete methods, using a custom query:

@Modifying
@Query("delete from Fruit f where f.name=:name or f.color=:color")
List<Fruit> deleteFruits(@Param("name") String name, @Param("color") String color);

Although the two solutions seem similar, and they do achieve the same result, 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.

5. Conclusion

In this article, we focused on derived Spring Data derived delete methods. The complete source code used in this article can be found over on GitHub.

Spring bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE