Create a Service and Controller using Spring Boot 3
Tags: java, springboot, restapi, spring
Published: 2024-01-17 02:18:54

Continue the implementation, let's use the repository class in the news service and controller classes. In this post you will see the API working

## Create the Service class
Create a class called **ProductService** inside the new folder **services**.

```
package com.spring.services;

import com.spring.models.Product;
import com.spring.repositories.ProductRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class ProductService {

    @Autowired
    private ProductRepository repository;

    @Transactional
    public Product create(Product product) {
        return repository.save(product);
    }

    public Product findById(Long id) {
        return repository.findById(id).orElse(new Product());
    }

    public List<Product> findAll() {
        return repository.findAll();
    }

    @Transactional
    public Product update(Product product, Long id) {
        var productPersisted = findById(id);
        if (!Objects.equals(productPersisted.getId(), id)) {
            return productPersisted;
        }
        BeanUtils.copyProperties(product, productPersisted, "id");

        return repository.save(productPersisted);
    }

    @Transactional
    public void delete(Long id) {
        repository.deleteById(id);
    }
}

```
This service class will take care about the methods create a product, update a product, find one specific or all products.
Reading the name of each method you can identify what it does.

This class is using the **ProductRepository** class to access the database information from product entity. The **@Autowired** annotation tells to the Spring takes care about the instancia. So we don't need to worry about that.

The **@Transactional** annotation is very important to guarantee  that if happened one error, the commit will dont make in the database.

If you remenber, we dont create the methods **findAll**, **findById**, **save** or **delete** to repository, but thanks to Spring this was created automatically when we used the **JpaRepository** inside the **ProductRepository**

## Create the Product DTO class
This class is to guarantee that the client can't informe value to id or any other information that inst insite the DTO classe.
Create a record class called **ProductCreateUpdate** inside the new folder **models/dto**.

```
package com.spring.models.dto;

public record ProductCreateUpdate(String name, Double price, String description) {
}
```
This DTO don't have the id information because we don't want the user change the value ou informe one when created.

## Create the Controller class
Create a class called **ProductController** inside the new folder **controllers**.

```
package com.spring.controllers;

import com.spring.models.Product;
import com.spring.models.dto.ProductCreateUpdate;
import com.spring.services.ProductService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("api/product")
public class ProductController {

    @Autowired
    private ProductService service;

    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE,
                produces = MediaType.APPLICATION_JSON_VALUE)
    public Product create(@RequestBody ProductCreateUpdate productCreate) {
        Product product = new Product();
        BeanUtils.copyProperties(productCreate, product);
        return service.create(product);
    }

    @GetMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Product findById(@PathVariable(value = "id") Long id){
        return service.findById(id);
    }

    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public List<Product> findAll(){
        return service.findAll();
    }

    @PutMapping(path = "/{id}",
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Product update(@PathVariable(value = "id") Long id, @RequestBody ProductCreateUpdate productUpdate) {
        Product product = new Product();
        BeanUtils.copyProperties(productUpdate, product);
        return service.update(product, id);
    }

    @DeleteMapping(path = "/{id}")
    public void delete(@PathVariable(value = "id") Long id){
        service.delete(id);
    }

}
```
This class will be responsible for connect the client with the application by HTTP connections.
In the start the class is using **@RestController** to inform that is a Rest API and **@RequestMapping** annotation to inform that the base path is **api/product**.

How we need to access the database information, the class has a var of type **ProductService**.

Before each explation of methods, we need understand what the next annotations do.

- **@RequestBody**: Inform that the information will come from the body of the request and of the type that variable.
- **@PathVariable**: Inform that the information will come from the specific part of the path an of the type that variable.
- **@PostMapping**: The request need be by HTTP Post
- **@GetMapping**: The request need be by HTTP Post
- **@PutMapping**: The request need be by HTTP PUT
- **@DeleteMapping**: The request need be by HTTP DELETE

In the first method **create** is about create a new product that data recived from the body in json format, the type of information is **ProductCreateUpdate**, will called the method create from the service and return the new product in json format.

The method **findById** is about find one specific product by the id informad in the path **api/product/{id}**, will called the method findById from the service and return the product a empty Product if not exist, both in json format.

The method **findAll** will find all products, will called the method findAll from the service and return a list of products in json format.

The method **update** is about update one existing product by the id informad in the path **api/product/{id}** with the updated data recived from the body in json format, the type of information is **ProductCreateUpdate**, will called the method update from the service and return the updated product in json format. If doesn't exist a product for the id informad, will return an empty product.

To finish with the last one, the **delete** is about delete a product by the id informad in the path **api/product/{id}**, will called the method delete from the service.

I know that the code can be improve, it will be done in the future.

## Testing the endpoints

For the test I'll use the Postman, it's easy do install.

![post create](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbf0jqlco9g3oseir5d7.png)
Here is creating a product by **POST** in **localhost:8080/api/product** with below body

```
{
    "name": "Mouse",
    "price": 50.00,
    "description": "Mouse Wireless 10000Dpi"
}
```

---

![get all](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/81rmgy7yxhwfts5o0b79.png)
Here is find all products by **GET** in **localhost:8080/api/product**

---

![get id](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucpm7i9a884k06qw5s5m.png)
Here is find a product with ID 1 by **GET** in **localhost:8080/api/product/1**

If you select all products in the database, you'll see the same information.

---

![put](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tf8kw00vada9tka7x51b.png)
Here is updating a product with ID 1 by **PUT** in **localhost:8080/api/product** with below body
```
{
    "name": "Mouse Professional",
    "price": 45.00,
    "description": "Mouse Wireless 8000Dpi"
}
```

Now in the database the information is updated.

---
![delete](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/plf38cf8j9zrvpwinb3l.png)
Here is delete a product with ID 1 by **DELETE** in **localhost:8080/api/product/1**

Now the product doesn't exist in the database anymore.

## Conclusion
In this post, we create the Service and Controller classes about Product, also we saw important annotations about the Rest

## Next Step
In the next step we will improve the API using ResponseEntity with the better response code and exception control.