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.
Here is creating a product by POST in localhost:8080/api/product with below body
{
"name": "Mouse",
"price": 50.00,
"description": "Mouse Wireless 10000Dpi"
}
Here is find all products by GET in localhost:8080/api/product
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.
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.
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.
Top comments (3)
can you please use the syntax highlighting in markdown?
Overall, this article is great, and I appreciate your effort in creating it. However, I would like to suggest a small addition to the section. I usually prefer utilizing the interface at the service layer. Why? The primary objective is to advocate for loose coupling and enhance flexibility in the codebase. This approach makes it easier to swap implementations or introduce new ones in the future. I believe this addition can serve as a valuable lesson for others. For more information, you can refer to this article. I hope this suggestion proves helpful.
Hi Ichwan
Thanks for help to improve this post and about your good article.
You are right, it's better create a interface and be implement by a class.
I'm only did that because the extension the article, but I'm doing the new smaller posts.