Lizard & Dog Blog

Spring Boot REST API – Part 2

The Gist

We’ve already implemented the basics of a Spring Boot API in Part 1 , but now we want to add additional functionalities to our existing API.

In this tutorial, we’ll go over the PUT and DELETE methods, and we’ll learn about other ways to handle database operations with Spring.

PUT and DELETE

The PUT method is a way to handle data transformation, if you want, for example to update the properties of an existing object:

Let’s go back to the method we implemented in part 1:

@PutMapping("/sellAnimal")
public void sellAnimal(@RequestParam(name="name", required=true)String animalName)
{

}

In this method, we’ll take an animal name as a parameter and update its sold value to true.

We’ll then move on to the service class to create the underlying method.

    public void sellAnimal(String animalName){
Animal animalToSell=animalRepository.findByName(animalName);
animalToSell.setSold(true);
animalRepository.save(animalToSell);
}

The find by name method is not yet implemented in the repository so we need to go one step deeper:

@Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
Animal findByName(String name);
}


We can then update our Controller Class with the following code:

    @PutMapping("/sellAnimal")
public void sellAnimal(@RequestParam(name="name", required=true)String animalName)
{
animalService.sellAnimal(animalName);
}

There are, however, two problems with this approach:

  1. The animal could have been already marked as sold;
  2. The animal could be absent from the list (i.e., wrong animal name)

We’ll need to refine our method in the service and repository to show that:

In the repository, we’ll define a method to find an animal by name if it hasn’t been sold yet:

@Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
Animal findByName(String name);
Animal findByNameAndSoldFalse(String name);
}

To inform the user of potential problems with the sale, we’ll include an informative message should the animal be already sold or non existent in the db. We thus modify our service class:

public String sellAnimal(String animalName){

Animal animalToSell=animalRepository.findByNameAndSoldFalse(animalName);
if(animalToSell!=null){
animalToSell.setSold(true);
animalRepository.save(animalToSell);
return 'Animal sold!';
}
else {
return "Animal doesn't exist or has already been sold";
}
}

We’ll also change our controller function to return a String that should be displayed to the user to see if the transaction was successful. Our final method is as follows:

@PutMapping("/sellAnimal")
public String sellAnimal(@RequestParam(name="name", required=true)String animalName)
{
return animalService.sellAnimal(animalName);
}

Let’s move on to the delete operation. In the case of our project, the delete function will be used if an animal has been donated instead of sold. It is then removed from the database. We begin by querying the repository by ID. This returns an optional value. Similarly as the PUT method, we’ll first verify that the animal is present before any further operation, to avoid errors.

public String removeAnimal(Long animalID){
Optional<Animal> animalToRemove=animalRepository.findById(animalID);
if(animalToRemove.get()!=null){
animalRepository.delete(animalToRemove.get());
return "Animal donated!";
}
else {
return "Animal doesn't exist or has already been sold";
}
}

We’re using the same sanity checks as the first case, but this time, we directly remove the animal from the database, and inform the user with a string of the transaction’s success.

We can then update our controller as follows:

@DeleteMapping("/removeAnimalFromInventory")
public String removeAnimal(@RequestParam(name="id", required=true)long animalID)
{
return animalService.removeAnimal(animalID);
}

We’ve gone over the POST, GET , PUT and DELETE methods. Now we’ll discuss other operations you could be doing with your database;

Additional db operations

Complex queries

You’ve maybe noticed that when we use the methods defined in the repository, they translate directly to psql instructions that are directly interpreted on the database.

If you want to handle more complex queries, however, you will have to use the @Query annotation in the repository. To demonstrate this, we’ll define a request where the user wants to list the first three unsold cats by order of price:

@Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
// The rest of your repository code goes here
Query("SELECT a FROM Animal a WHERE a.species = 'cat' AND a.isSold = false ORDER BY a.price LIMIT 3")
List<Animal> findFirstThreeUnsoldCats();
}

Inside the query annotation you’ll notice an expression not too dissimilar with a regular PSQL query, which would be in this case:

SELECT * FROM animals WHERE species = 'cat' AND sold = false ORDER BY id LIMIT 3;

You can use the @Query annotation along with the @Modifying annotation to define methods on the repository that UPDATE or DELETE a line:

@Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
// The rest of your repository code goes here
@Modifying
@Query("UPDATE Animal a SET a.sold = true WHERE a.species = 'cat' AND a.sold = false ORDER BY a.id")
void updateFirstThreeUnsoldCats();}

I’ll leave it up to you as an exercise to implement a delete query that deletes the animals instead of updating them

Pagination

Spring boot also allows to return your content in “Pages”, which is a good way to organise your data if it’s meant to be read in a website for example. To do so, implement the following line in your repository:

Page<Animal> findAll(Pageable pageable);

In your service class:

    public Page<Animal> findAllPaged(Pageable pageable) {
return animalRepository.findAll(pageable);
}

And in the controller:

@GetMapping("/paged")
public Page<Animal> getAnimalsPaged(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
PageRequest pageable = PageRequest.of(page, size);
return animalService.findAllPaged(pageable);
}

This allows you to display all the animals in pages. To display them you’d pass the page number and the size of the page as parameters and this basically returns a list of the specific size of the chosen elements.

Headers vs Params

So far, we’ve been using params for our requests. Spring also allows to specify headers for a request. Here are the main differences between parameters and headers:

  1. Headers:
    • Location: Headers are part of the HTTP request and response. They contain metadata or additional information about the request or response.
    • Purpose: Headers carry information such as content type, encoding, authentication tokens, and other metadata that is not directly related to the content of the request or response.
    • Usage: Headers are often used for controlling the behavior of the request or conveying information that applies to the entire request or response.
  2. Parameters:
    • Location: Parameters are part of the URL in the case of query parameters (in the query string), or part of the request body in the case of form parameters or JSON payloads.
    • Purpose: Parameters provide input data to the server. They are used to send information related to the specific operation or resource being accessed.
    • Usage: Parameters are commonly used in API endpoints, form submissions, and dynamic URL routing. They help customize the behavior of a specific request.

To work with headers, instead of params, you can do like so (here in the case of the buyAnimal method:

@PostMapping("/buyAnimal")
public void buyAnimal(@RequestHeader(name="name", required=true)String name,
@RequestHeader(name="species", required=true)String species,
@RequestHeader(name="price", required=true) double price )
{
animalService.buyAnimal(name, species, price);
}

Conclusion and next steps:

This tutorial should help getting you started with Spring Boot REST API development. A good next step would be to look into Spring Security, which is a framework that adds a layer of Security to your apps.

You can also access the full code of the tutorial on our GitHub:

https://github.com/lizardanddog/spring_tutorial_repository

If you have any further queries, feel free to ask us at:

www.lizardanddog.com

Leave a comment

Create a website or blog at WordPress.com