Skip to content

3. Creating API's REST

1. Spring layers

We are learning about some design patterns that we are going to use in our application, and that are necessary to define a correct architecture in a Spring application. Therefore, the first thing we will do is define the layers of our application structure, and once we define it we will define new design patterns that we will integrate our app.

![SpringLayers](./img/SpringLayers.png){width=95%}

The idea is to create a package structure that groups the classes into 6 main packages: package that contains the main class, web layer that contains the controllers, data access layer that contains the repository, service layer, data model layer and dto layer. All these are included, basically in 3: web, service and repository.

The objective is that, with a well-defined and coupled architecture it is possible to use Dependency Injection, since it will facilitate communication and access between the different layers.

1.1. Main Class

Every Java application must contain a main class with a main method. Said method, in case of implementing an application with Spring, must call the run method of the SpringApplication class. We will leave this class in the root by default, so that we will always have it in the same place.

1.2. Web Layer. Controller Layer

Let's now define the behavior of the application by implementing the rest of the classes. We are going to start with the highest level layer, the controllers layer, where we will expose the application's services. In our application it will be called Controller.

This layer will basically work in 3 ways:

  • Web services consumed by applications (REST or SOAP service).
  • Views implemented with JSP, JSF, Thymeleaf, etc. This is our main case, although we will also create REST services.
  • Views with frameworks such as Vaadin, Wicket, ZK, etc.

1.3. Services Layer

Layer that is responsible for implementing the business logic, that is, all the tasks that our system is capable of performing.

This is one of the most important layers since all data validation operations that refer to business logic (for example, checking that a current account has a balance when making a payment) and security will be carried out here. It will be called service.

Typically, they access the data stored in the application's database through the repositories, do a series of operations, and send the data to the controller. We can find the following types of service:

  • Repository Integrity Services: they are in charge of consuming information from the repository. They are easy to implement services (for example, request a list of clients).
  • Business Operability Services: they carry out specific operations for the business flow (they carry out complex operations to complete a transaction, such as a sale, storing an order, etc).
  • Security Services: dedicated to carrying out security operations
  • Management Services: dedicated to generating reports and/or statistics.

1.4. Repository layer

Repositories are the classes in charge of managing access to data. It typically contains classes that perform CRUD operations using only an entity class from a domain model. It will be called repository.

1.5. Model layer

It will contain the mappings of the database tables into classes that represent entities. The layer will be named model.

1.6. DTO layer

Controllers usually handle DTO instead of pojos or beans, due to the structure of the API or rendering in views. Therefore, we will need to implement a two-way conversion between a pojo and a DTO model. Later we will define, in the design patterns, what a DTO is and we will see the bidirectional conversion, called mapping. In addition, these DTO's will be in a separate layer called dto.

![SpringLayers2](./img/SpringLayers2.png){width=65%}

2. Design patterns

A design pattern is a proven solution that solves a specific type of design problem in software development. There are lots of design patterns that are divided into categories, for example: creation, structural, behavioral, interaction, etc.

Why use design patterns?: They allow you to have the code well organized, readable and maintainable, it also allows you to reuse code and increases scalability in your project.

In themselves they provide a standard terminology and a set of good practices regarding the solution of software development problems.

We are going to explain several of them to begin to understand what design patterns are.

2.1. MVC pattern.

We have already explained this pattern in the previous topic, but we are going to give it a little review.

It allows separating an application into 3 layers, a way of organizing and making a project scalable. The layers that we can find are:

  • Model: This layer represents everything that has to do with data access: saving, updating, obtaining data, as well as all the business logic code, basically the Java classes and part of the business logic.
  • View: The view has to do with the data presentation of the model and what the user sees, usually a view is the visual representation of a model (POJO or java class). For example, the user model, which is a class in Java and whose properties are first name and last name, must belong to a view in which final user sees those properties.
  • Controller: The controller is in charge of connecting the model with the views, it works as a bridge between the view and the model, the controller receives events generated by the user from the views and is in charge of directing the respective request to the model. For example, the user wants to see the customers with the last name Álvarez, the request goes to the controller and it takes care of using the appropriate model and returning that model to the view.

At no time the view will directly interact with the model, this also maintains security in an application.

The important thing about this pattern is that it allows you to divide it into parts, which are somehow independent, so if, for example, a change is made to the model, it would not affect the view or if there is a change, it would be minimal.

2.2. DTO Pattern

With this pattern, one of the transversal layers of architecture is designed. It solves the problem of how to allow a client to exchange data with the server without making multiple calls asking for each piece of data. For example, if we have an entity called Person and an entity called Addresses, when asking for the people and their addresses we have to make multiple calls to the server to ask for the people and the addresses of each person, building the view with this information.

DTO solves it by passing a lightweight object to the client with all the necessary data, together. The client can then make local requests to the object it has received.

To do this, Java classes are created that encapsulate the data in a package that can be transported over the network (they can implement java.io.Serializable, although it is not mandatory), that is, with the previous example, we would create a Java class that would carry the person and their addresses, all together in the same object.

These objects are used in all layers of the application, so the information is carried by all layers of the application. It is recommended to always fill in all DTO fields to avoid NullPointerException errors (an empty string may be better), make DTOs self-describing, use arrays or collections of DTOs when necessary, and consider methods that override equals().

There are two variants of DTO's:

  • Custom DTOs that represent part of a bean or bundle multiple beans.
  • Domain DTOs called "entities". A domain class is not directly accessible by the client, since, due to the separation of the MVC pattern from the view, the entities that map the database (which are the entities) cannot be accessed. For that reason, DTO copies of server domain objects (entities) are made. Clients can operate on local copies improving read and update performance.

With all this, we can summarize that DTO is a very effective pattern to transmit information between a client and a server, since it allows us to create data structures independent of our data model (Entities), which allows us to create as many "views" as necessary. From a set of tables or data sources. In addition, it allows us to control the format, name and types of data with which we transmit the data to adjust to a certain requirement. Finally, if for some reason the data model changes (and with it the entities) the client will not be affected, since they will continue to receive the same DTO.

Next we will see how to implement the DTO pattern.

2.3. DAO pattern

The Data Access Object (DAO) pattern, which allows separating the data access logic from the Business Objects, in such a way that the DAO encapsulates all the data access logic to the rest of the application. This proposal proposes to completely separate the business logic from the logic to access the data, in this way, the DAO will provide the necessary methods to insert, update, delete and consult the information; on the other hand, the business layer only cares about business logic and uses the DAO to interact with the data source. In the example that we are going to design below, we will see how to implement the DAO pattern.

2.4. FACADE pattern

The Facade design pattern simplifies the complexity of a system through a simpler interface. Improves access to our system by allowing other systems or subsystems to use a common access point that reduces complexity, minimizing interactions and dependencies. That is, we will create a Java interface that will have the method headers as a common access point, while there will be Java classes that will implement that interface.

Throughout this example we will make use of this pattern.

3. Creating the project and configuring Hibernate

In this section we are going to create a simple Hibernate program with Spring, to apply all we have studied in previous units with Spring. We only have to create a new project, selecting dependencies that we usually use:

![SpringRest](./img/SpringRest.png){width=95%}

This will generate pom.xml with all we need

We need to set the parameters that we set in hibernate.cfg.xml in application.properties file:

Bash
# puerto de escucha
server.port = 8090

# Conexión con la BBDD
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Datos del SGBD. REVISAR !!!!
spring.datasource.url = jdbc:mysql://localhost:3308/AD_UD5_Clientes
spring.datasource.username = root
spring.datasource.password = root


# configuración de hibernate (simple)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# hbm2DDL. values= none, validate, update, create, and create-drop
spring.jpa.hibernate.ddl-auto=validate

# import.sql file under src/main/resources/
# or data.sql or schema.sql

# respetar mayúsculas y minúsculas
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Important

All options of database initialization can be found in database initialization.

3.1. The model

Let's go to use this model to our practice:

![DBModel](./img/DBModel.png){width=95%}

Warning

You have a script to create full database linked here. In this unit we do not implement about Movimientos.

To create the model we have to create the Beans that we create in unit 3. We can use Lombok to improve our development time.

Tip

You con mark an attribute with Lombok annotation @ToString.Exclude to avoid participate in toString method, for instance to avoid recursion.

Cliente DAO in package model:

Java
@Data
@Entity
@Table(name = "clientes")
public class Cliente {

    @Id
    @GeneratedValue( strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name = "nif")
    private String nif;

    @Column(name = "nombre")
    private String nombre;
    private String apellidos;

    @Column(name = "claveseguridad")
    private String claveSeguridad;
    private String email;

    @OneToOne(fetch = FetchType.LAZY, 
            cascade =  CascadeType.ALL, 
            mappedBy = "cliente")
    @ToString.Exclude
    private Recomendacion recomendacion;

    @OneToMany(fetch = FetchType.LAZY, 
            cascade = CascadeType.ALL, 
            mappedBy = "cliente")
    @ToString.Exclude
    private List<Cuenta> listaCuentas;

  @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
  @JoinTable(name="clientesdirecciones", 
              joinColumns=@JoinColumn(name="idcliente"),
              inverseJoinColumns=@JoinColumn(name="iddireccion"))
  @ToString.Exclude
  private List<Direccion> listaDirecciones;

3.2. The DTO

The DTO's (Data Transfer Object) serves to transfer data in our system through transactions carried out by our entities from one operation to another without losing the integrity between the data.

For this reason, it is important to define that data access is done only through DAO (Data Access Object) our model, thereby obtaining an abstraction of the data model. The data is only accessed through methods defined in the DAO. The DAOs include another concept that is the DTO (Data Transfer Object). DTO's are a class of objects that serve solely to transport data. DTO contains the properties of the object. Data that may originate from one or more information entities.

Another good practice is to mark the classes with the DTO suffix to remember its meaning, so the Client class would remain as ClientDTO.

One of the characteristics of the DTO's is that they must be Serializable objects in order to be able to travel through the network. We need to indicate this feature in the DTO's, so we are going to add that they implement the Serializable interface, and with it, the UID property that identifies the version of each transported object.

To create a DTO we will create a class with the attributes that we want containing the DTO. It can be more or less than DAO class. Then is important to create methods to convert either between DAO to DTO as DTO to DAO. These methods can be created in a static way.

Java
// Convierte una entidad DAO a un objeto DTO con todos los datos
public static ClienteDTO convertToDTO(Cliente cliente) {
  // Creamos el clienteDTO y asignamos los valores basicos
  ClienteDTO clienteDTO = new ClienteDTO();
  clienteDTO.setIdCliente(cliente.getId());
  clienteDTO.setNif(cliente.getNif());
  clienteDTO.setNombre(cliente.getNombre());
  clienteDTO.setApellidos(cliente.getApellidos());

// rest of attributes

  return clienteDTO;
}       

    // Convierte de un objeto a un entidad
    public static Cliente convertToEntity(ClienteDTO clientedto) {
    Cliente  cliente = new Cliente();
        cliente.setId(clientedto.getIdCliente());
        cliente.setNif(clientedto.getNif());

    // rest of attributes

    return cliente;

As we say, we prevent to access entities from higher layers.

More...

You can search the internet for information about ModelMapper to manage DTOs.

3.2.1. Converting DTO to JSON

As we have said, these objects (DTO's ) are parsed to JSON automaticaly, but how? Spring uses Jackson object mapper to transform objects to JSON Objects. In this tutorial you can understand better how it works.

Parsing objects to JSON can take us to another infinite recursion problem, when object contains cross-references, like toString() methods. We have learnt how to avoid the StackOverflowException in toString() methods, using Lombok @ToString.Exclude, but how to avoid when parsoing to JSON. The solution is offered with new annotations, as follows:

  • @JsonIgnore → this field will not be parsed.
  • @JsonManagedReference → tell that we will show this object information in forward way, but not in a backward mode. This annotation will be complemented by:
  • @JsonBackReference → Show the referenced information only, similar of @JsonIgnore.
  • @JsonIgnoreProperties("property") → skip this property in the annotated field.

If we mark:

Java
class Director{
  // own attributes

  @JsonManagedReference
  Set<Film>
}

class Film{
  // own attributes

  @JsonBackReference
  Director;
}

We will show the Film information of a Director without recurssion (only own attributes)

You can find more information here

3.3. The repository

The repository layer is responsible for managing access to data. This makes it possible to separate the business logic from the data access, thus allowing, for example, to be able to modify the system database without affecting the logic.

In this layer we must distinguish two types of access:

  • Access to data from the system's own data model that will be done through DAO (Data Access Object) access. For this type of access, we will use the JPA (Java Persistence API) framework through Spring Data.
  • Access to external systems through connectors (webservices, APIs, etc.)

For this reason we are going to create an interface in which we have the operations that we are going to expose. This interface will be ClientRepository.java and will be created in the repository package.

With Spring, we will save a lot of work, since we will simply define a working interface, in which we will indicate the type of repository that we want to create, the class on which it is going to work and the type of data that works as an identifier of said class:

Spring provides the Repository Interface, from which it inherits CrudRepository, which includes the definition of the basic CRUD operations. From the latter it inherits PagingAndSortingRepository, which adds paging and ordering functions, and finally we have JPARepository, which includes specific operations for JPA.

The importance of the generic definition of Repository<Class,Type> is that all the objects it will retrieve are of that class, and the type indicates the type of the primary key of that class. Following our example, the repository definition would be:

Java
1
2
3
4
5
@Repository
@Transactional
public interface ClienteRepository extends JpaRepository<Cliente, Long> {

}

With this, Spring already allows us to access the database and perform basic operations. The following methods are implemented by default, and we will not need to implement them, just define them:

  • Recover data:
  • findAll(), findById(Id), findById(Iterable<Id>):retrieves one, or all, occurrences of an identifier or a collection of identifiers.
  • Delete data:
  • delete(Object), deleteAll(), deleteById(Id), deleteAllById(Iterable<Id>):delete by object, identifier or all.
  • count and check:
  • count(), existsById()
  • save objects:
  • save(Object), save(Iterable<Object>):save the object(s)

Tip

If we need another method, we must define it, and then create a new class to implement this interface and implement the method.

3.4. The service

The service layer manages the business logic of our application. This business logic is separate from the web logic, which is in the controller.

When we define classes that implement services for business logic, the following rules must be followed:

  1. Define an interface that will have the headers of the methods that you want to publish. In this way we make use of the Facade pattern and expose the methods of the service to use.
  2. We define a class (bear in mind that an interface can have several classes that implement it) that implements the interface, so that we can implement all the service methods following the required business logic.
  3. The @Service annotation tells Spring to recognize the class as a service (similar to the @Controller annotation that we have studied in previous section).
  4. We will use @Autowired to inject the service into the controller (we will see later).
  5. We will use @Autowired to inject DAO with which we are going to work into the service.
  6. Keep in mind that a service method will define a business level operation, eg giving a welcome message. Service methods will be made up of other smaller operations, which will be defined in the repository layer.

With all this, we are going to begin to define the service layer. So we start by creating an interface called ClientService.java in the service package.

Java
1
2
3
4
5
6
7
8
public interface ClienteService {

    void saveCliente(ClienteDTO clienteDTO);
    ClienteDTO getClienteById(Long id);
    List<ClienteDTO> listAllClientes();
    void deleteCliente(Long id);

}

Notice that we define in this case 4 basic operations with ClienteDTO, due to is the objects will manage the controller.

Once we have the interface, we create a new class called ClientServiceImpl.java, which will be in charge of implementing the methods that are declared in the interface. After that, we make the new class extend the created interface and after that we make it implement all the methods of the interface by default. We also indicate the @Service annotation to the class. It will be as follows:

Java
@Service
public class ClienteServiceImpl implements ClienteService{

    @Autowired
    private ClienteRepository clienteRepository;

    @Override
    public void saveCliente(ClienteDTO clienteDTO) {
        Cliente cliente = ClienteDTO.convertToEntity(clienteDTO);
        clienteRepository.save(cliente);
    }

    @Override
    public ClienteDTO getClienteById(Long id) {
        Optional<Cliente> cliente = clienteRepository.findById(id);
        if(cliente.isPresent()) {
            return ClienteDTO.convertToDTO(cliente.get());
        }else {
            return null;            
        }
    }

    @Override
    public List<ClienteDTO> listAllClientes() {
        List<Cliente> lista = clienteRepository.findAll();
        List<ClienteDTO> listaResultado = new ArrayList<ClienteDTO>();
        for (int i = 0; i < lista.size(); ++i) {
            listaResultado.add(ClienteDTO.convertToDTO(lista.get(i)));
        }
        return listaResultado;
    }

    @Override
    public void deleteCliente(Long id) {
        clienteRepository.deleteById(id);
    }
}

As you can see, the service is in charge of invoking the repository methods, and performing some checks if necessary. Furthermore, we get Cliente objects and transform it to ClienteDTO, in order to use it to return to the controller.

Important

findClienteById() method returns a new wrapper class Optional<Cliente>. This class wraps an Object that will exist or no, providing methods to check it and act in consequence:

  • isPresent() → returns a boolean.
  • orElse(anotherObject) → if not exists, return another Object instead of Optional.
  • get() → returns existing object.

The main goal is to avoid famous NullPointerException. More information can be found here

3.5. The controller

We are now going to define the highest level layer, the controllers layer, where we will expose the application's services.

The controller will be in charge of responding to user requests with the application. It will include the services, and in case of creating an MVC application, it will be able to invoke template engines, such as Thymeleaf. As we have commented here we will implement it in a single class (without following the previous pattern).

The controller will invoke the service associated with said request and return the data obtained or the response to the same client. We must mark the class with the @Controller stereotype. In the case of REST services, we must also indicate that the returns of the class methods are serialized to JSON, and we achieve this with @ResponseBody. Since Spring 4, the two annotations have been merged into one, via @RestController. We will only leave @Controller for projects where we return a view (HTML + CSS + JS).

The service that we are going to create will have a complete behavior in terms of Cliente maintenance, as well as their recommendation, so we can list customers, view their information, register customers, update customer data and delete customers, or be the CRUD operations that are known.

So we'll start by creating a controller called ClienteController.java. This controller will implement the 4 operations corresponding to CRUD: create, read, update and delete. Apart from these 4 we will create an operation that will show the initial page of the application, with a link to customer maintenance.

Java
1
2
3
4
5
6
7
@RestController
public class ClienteController {

    @Autowired
    private ClienteService clienteService;

  // mapping the requests. One method per route/request

The controller normally defines four CRUD operations, but we can add all that we need. Let's go to see it.

3.5.1. Index

This method maps the root index of our webapp, For instance, we can send name of the application and module. These variables could be defined in our application.properties and can be loaded with @Value annotation and a String ${property_name} inside.

Java
1
2
3
4
5
6
7
8
@Autowired
    private HttpServletRequest context;

    @Value("${app.name}")
    private String appName;

    @Value("${developer.name}")
    private String devName;

The index controller normally shows general information about the main page, as follows:

Java
1
2
3
4
5
6
7
@GetMapping("/")
public String index() {
  String res="Hello from Spring\n";
  res+="You are running " +appName+"\n";
  res+="Developed by " +devName;
  return res;
}

4. Read operations (GET)

It's time to retrieve data from our server, and normally GET operations are the most demanded methods. We are going to implement several methods, that could return one or many objects of the desired class, in our notes, Cliente class.

4.1. Get All

No filter option is needed, because we want to get all clients. When we get this get request, we must call to listAll method in our service, who calls, indeed to findAll in our repository.

Java
1
2
3
4
5
6
@GetMapping("/clientes")
  public List<ClienteDTO> listClientes(){
    myLog.info(context.getMethod() + " from " + context.getRemoteHost());
    List<ClienteDTO> losClientes=clienteService.listAllClientes();
    return losClientes;
} 

Apart from log message, we simply retrieve data from the service and return it to the response.

4.2. Get One

This is the most specific version, and normally in the request we look for an object from its ID. Then, we must get a parameter in our request, and we will use the ID in the path, using @PathVariable annotation.

Java
1
2
3
4
5
6
@GetMapping("/clientes/{idCliente}")
public ClienteDTO showClienteById(@PathVariable Long idCliente){
  myLog.info(context.getMethod() + " from " + context.getRemoteHost());
  ClienteDTO elCliente=clienteService.getClienteById(idCliente);
  return elCliente;
} 

The request will be /clientes/7, for instance, and then, in our controller method the parameter idClientewill be set with 7 value. We call getClienteById(7) and, obviously we get the ClienteDTO encapsulating the requested Cliente, if exists.

We can improve the methods for the case that there are no results (Cliente does not exists) or an error has occurred, returning and encapsulating the results in a ResponseEntity<Cliente>. This wrapper class returns the result to the request, but allows adding an argument that will be the http status code. This code can be captured on the client application for error handling. The algorithm will be something like this:

Java
1
2
3
4
5
6
7
public ResponseEntity<results>controllerMethod(){
  // collect the data from the service/repository
  if(!mistake) {
    return new ResponseEntity<>(Results,HttpStatus.OK); // ALL GOOD
  }
    return new ResponseEntity<>(HttpStatus.NOT_FOUND); // SOMETHING IS WRONG
  }

applied to our sample application, and merging last two code samplers, will get as follows:

Java
1
2
3
4
5
6
7
8
9
@GetMapping("/clientes/{idCliente}")
public ResponseEntity<ClienteDTO> showClienteById(@PathVariable Long idCliente){
  myLog.info(context.getMethod() + context.getRequestURI() + " from " + context.getRemoteHost());
  ClienteDTO elCliente=clienteService.getClienteById(idCliente);
  if (elCliente==null)
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
  else
    return new ResponseEntity<>(elCliente,HttpStatus.OK);
} 

4.3. Handling exceptions

The controller handle several exceptions, and the program continues running unless a big crush occurs. Now we present a method that will give a response when an exception happens. We will annotate this method with @ExceptionHandler, passing the exception class that we must catch

Java
1
2
3
4
5
6
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<String> handleError(MethodArgumentTypeMismatchException e) {
  myLog.warn("Method Argument Type Mismatch", e);
  String message = String.format("Method Argument Type Mismatch: %s", e.getName());
  return new ResponseEntity<>(message,HttpStatus.BAD_REQUEST);
}

We can handle several exceptions in one unique method, marking it with a collection of exceptions, for instance as:

Java
@ExceptionHandler({
    MissingServletRequestParameterException.class,
    MethodArgumentTypeMismatchException.class,
    InvalidStreamDefinitionException.class
})
public ResponseEntity<String> handleError(Exception e) {
  // method body
} 

// i aquest quan no es troba una entrada al controlador
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Map<String, Object>> handleNotFoundError(NoHandlerFoundException ex) {
    Map<String, Object> response = new HashMap<>();
    response.put("error", "404 Not Found");
    response.put("message", "El recurso solicitado no existe.");
    response.put("path", ex.getRequestURL());

    return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}

5. Save operations (POST)

First operation we need to do is to store new Cliente on our database. Let's go to explain by example. The controller will receive a new DTO object sent by the client app (we will use Postman, as you can view in Appendix 1):

Java
//@PostMapping(value="/clientes",consumes={"application/json"})
@PostMapping("/clientes")
public ResponseEntity<ClienteDTO> addDirector(@RequestBody ClienteDTO newCliente) { 
  myLog.info(context.getMethod() + context.getRequestURI()); 
  ClienteDTO elCliente= clienteService.saveCliente(newCliente);
    if (elCliente==null)
      return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    else
    return new ResponseEntity<>(elCliente,HttpStatus.OK);
}

notice that:

  • The request is mapped with @PostMapping. Here you have a comment with extra options.
  • The object received by the app is inside a JSON object in the request body, marked as @RequestBody. In this unit we are going to suppose that the data arrives well formatted.
  • Data received is passed to service and a new (stored) Cliente is returned.
  • This client is returned to the client

In this picture you can view data sent to API and returned value. Notice that Cuentas and Direcciones are not present in that data. We will see later how to add this additional data.

![Post_Cliente](./img/Post_Cliente.png){width=95%}

Warning

There is a lot of validations to do in order to verify data integrity. Here you can find an article about ho to validate data recieved from client applications.

6. Update operations (PUT)

To update an object of the database, we need to receive the update (and complete) object in the request. We can not save it immediately, because it could not exist. For this reason we must check the existence, and if this is positive, then save the object received, that will update previous version in the database. Let's go to see the sample:

Java
@PutMapping("/clientes")
public ResponseEntity<ClienteDTO> updateCliente(@RequestBody ClienteDTO updCliente) {   
    myLog.info(context.getMethod() + context.getRequestURI());
    // buscamos si existe previamente
    ClienteDTO elCliente= clienteService.getClienteById(updCliente.getIdCliente());
      if (elCliente==null)
          return new ResponseEntity<>(HttpStatus.NOT_FOUND);
      else {
        // como ya sabemos que existe, save actualiza
        ClienteDTO elClienteUPD= clienteService.saveCliente(updCliente);
        return new ResponseEntity<>(elClienteUPD,HttpStatus.OK);
      }
}

7. Delete operations (DELETE)

Delete is very simple operation, because we only need the object's identifier that we want to delete, and then we could get from the path, inside the path variable. Then, we must call th delete operation of the service.

Java
1
2
3
4
5
@DeleteMapping("/clientes/{idCliente}")
public ResponseEntity<String> deleteCliente(@PathVariable Long idCliente){
  clienteService.deleteCliente(idCliente);
  return new ResponseEntity<>("Cliente borrado satisfactoriamenet",HttpStatus.OK);
} 

8. Exercise. Complete the server

We have finished creating our controller about Cliente class, but we can improve our application adding more services and configurations to the server. This concepts will be studied in next sections.

We recommend completing Cuenta and Direccion controller, services and repositories with default operations.

Furthermore, you can add options to add a Cuenta to a Cliente or remove, and same as Direccion