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")
int 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.
Also, the deleteBy method can return the list of deleted records, while the custom query will return the number of deleted records.
5. Conclusion
In this article, we focused on derived Spring Data derived delete methods.
