API REST 5. Hypermedia

Introducción

Hypermedia es la interrelación entre recursos mediante enlaces. La idea es sólo conocer un recurso principal y de ahí descubrir como acceder al resto de los recursos. Esto permite aumentar la interoperabilidad entre recursos con mucha menos información.

Por ejemplo:

Tenemos la tabla persona donde podemos consultar edad, nombre y sexo. En otra tabla  grupo se almacena el grupo al que pertenece la persona. En otra tabla país se almacena el país al que pertenece la persona.  Si necesitamos consultar los datos, grupo y país de una persona tendríamos que consumir los recursos,

http://localhost:8080/personah/3

De los resultados obtenemos el id del grupo. Para conocer el nombre del grupo,

http://localhost:8080/grupoh/2

De los resultados obtenemos el id del país. Para conocer el nombre del país,

http://localhost:8080/paish/2

Usando hypermedia podríamos presentar:

Y navegaríamos por los link para obtener toda la información requerida sin necesidad de conocer cada uno de los recursos.

Programar hypermedia

Crear Entity

Creamos los entity PersonaHateoas, GrupoHateoas y PaisHateoas.

PersonaHateoas

package com.mio.administrar.entity;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class PersonaHateoas implements Serializable  {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int idPersona;

	private int idGrupo;
	private int idPais;
	private int edad;
	private String nombre;
	private String sexo;

	@ManyToOne
	@JoinColumn(name="idGrupo",insertable=false,updatable=false)
	private GrupoHateoas grupoHateoas;
	
	@ManyToOne
	@JoinColumn(name="idPais",insertable=false,updatable=false)
	private PaisHateoas paisHateoas;
	
	public PersonaHateoas() {
	}

	public int getIdPersona() {
		return this.idPersona;
	}

	public void setIdPersona(int idPersona) {
		this.idPersona = idPersona;
	}

	public int getEdad() {
		return this.edad;
	}

	public void setEdad(int edad) {
		this.edad = edad;
	}

	public String getNombre() {
		return this.nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public String getSexo() {
		return this.sexo;
	}

	public void setSexo(String sexo) {
		this.sexo = sexo;
	}

	public int getIdGrupo() {
		return idGrupo;
	}

	public void setIdGrupo(int idGrupo) {
		this.idGrupo = idGrupo;
	}

	public int getIdPais() {
		return idPais;
	}

	public void setIdPais(int idPais) {
		this.idPais = idPais;
	}
}

GrupoHateoas

package com.mio.administrar.entity;

import java.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="grupoHateoas")
public class GrupoHateoas implements Serializable  {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int idGrupo;

	private String nombre;

	@OneToMany(mappedBy="grupoHateoas")
	private List<PersonaHateoas> personaHateoas;
	
	public GrupoHateoas() {
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public List<PersonaHateoas> getPersonaHateoas() {
		return this.personaHateoas;
	}
}

PaisHateoas

package com.mio.administrar.entity;

import java.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="paisHateoas")
public class PaisHateoas implements Serializable  {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int idPais;

	private String nombre;

	@OneToMany(mappedBy="paisHateoas")
	private List<PersonaHateoas> personaHateoas;
	
	public PaisHateoas() {
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public List<PersonaHateoas> getPersonaHateoas() {
		return this.personaHateoas;
	}
}

Crear Repository

Creamos los repository PersonaHateoasRepository, GrupoHateoasRepository y PaisHateoasRepository.

PersonaHateoasRepository

package com.mio.administrar.repository;

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;
import com.mio.administrar.entity.PersonaHateoas;

@Transactional
public interface PersonaHateoasRepository extends CrudRepository<PersonaHateoas, Long>{
	@Query("select p from PersonaHateoas p where p.idPersona = ?1")
	PersonaHateoas unaPersona(int id);
	
	@Query("select p from PersonaHateoas p")
	List<PersonaHateoas> allPersonas();
}

GrupoHateoasRepository

package com.mio.administrar.repository;

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;
import com.mio.administrar.entity.GrupoHateoas;

@Transactional
public interface GrupoHateoasRepository extends CrudRepository<GrupoHateoas, Long>{
	@Query("select g from GrupoHateoas g where g.idGrupo = ?1")
	GrupoHateoas unGrupo(int id);
	
	@Query("select g from GrupoHateoas g")
	List<GrupoHateoas> allGrupos();
}

PaisHateoasRepository

package com.mio.administrar.repository;

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;
import com.mio.administrar.entity.PaisHateoas;

@Transactional
public interface PaisHateoasRepository extends CrudRepository<PaisHateoas, Long>{
	@Query("select p from PaisHateoas p where p.idPais = ?1")
	PaisHateoas unPais(int id);
	
	@Query("select p from PaisHateoas p")
	List<PaisHateoas> allPaises();
}

Crear Controller

Creamos los repository PersonaHateoasController, GrupoHateoasController y PaisHateoasController.

PersonaHateoasController

package com.mio.administrar.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resources;
import org.springframework.hateoas.Resource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.mio.administrar.entity.PersonaHateoas;
import com.mio.administrar.repository.PersonaHateoasRepository;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping(path="/personah", produces = "application/hal+json")
public class PersonaHateoasController  {
	@Autowired
	private PersonaHateoasRepository personaHateoasRepository;
	
	/**
	 * Obtiene todas las personas: GET http://{servidor}:{puerto}/personah
	 * @return Lista de personas
	 */
	@RequestMapping(method = RequestMethod.GET)
	public Resources<Resource<PersonaHateoas>> allPersonas() {
		List<PersonaHateoas> listPersonaHateoas = personaHateoasRepository.allPersonas();
		return personaToResource(listPersonaHateoas);
	}
	
	 /**
	 * Obtiene una person por id: GET http://{servidor}:{puerto}/personah/{id}
	 * @param id Id de la persona. En la Url
	 * @return Una persona
	 */
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public Resource<PersonaHateoas> unaPersona(@PathVariable int id) {
		PersonaHateoas	personaHateoas	= personaHateoasRepository.unaPersona(id);
		return personaToResource(personaHateoas);
	}
	
	/**
	 * Recorre una lista de Personas para agregar links
	 * @param personas Lista de personas
	 * @return Resources
	 */
	private Resources<Resource<PersonaHateoas>> personaToResource(List<PersonaHateoas> personas) {
		Link selfLink = linkTo(methodOn(PersonaHateoasController.class).allPersonas()).withSelfRel();
		List<Resource<PersonaHateoas>> customerResources = personas.stream().map(persona -> personaToResource(persona)).collect(Collectors.toList());
		return new Resources<>(customerResources, selfLink);
	}

	/**
	 * Agrega links a Una persona
	 * @param persona Una persona
	 * @return Resource
	 */
	private Resource<PersonaHateoas> personaToResource(PersonaHateoas persona) {
		Link selfLink 	= linkTo(methodOn(PersonaHateoasController.class).unaPersona(persona.getIdPersona())).withSelfRel();
		Link grupoLink 	= linkTo(methodOn(GrupoHateoasController.class).unGrupo(persona.getIdGrupo() )).withRel("Grupo");
		Link paisLink 	= linkTo(methodOn(PaisHateoasController.class).unPais(persona.getIdPais() )).withRel("Pais");
		return new Resource<>(persona, selfLink,grupoLink,paisLink);
	}
}

GrupoHateoasController

package com.mio.administrar.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.mio.administrar.entity.GrupoHateoas;
import com.mio.administrar.repository.GrupoHateoasRepository;

@RestController
@RequestMapping(path="/grupoh", produces = "application/hal+json")
public class GrupoHateoasController  {
	@Autowired
	private GrupoHateoasRepository grupoHateoasRepository;
	
	/**
	 * Obtiene todos los grupos: GET http://{servidor}:{puerto}/grupoh
	 * @return Lista de grupos
	 */
	@RequestMapping(method = RequestMethod.GET)
	public Iterable<GrupoHateoas> findAll() {
		return grupoHateoasRepository.findAll() ;
	}
	
	/**
	 * Obtiene un grupo por id: GET http://{servidor}:{puerto}/grupoh/{id}
	 * @param id Id del grupo. En la Url
	 * @return Un grupo
	 */
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public GrupoHateoas unGrupo(@PathVariable int id) {
		return grupoHateoasRepository.unGrupo(id);
	}
}

PaisHateoasController

package com.mio.administrar.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.mio.administrar.entity.PaisHateoas;
import com.mio.administrar.repository.PaisHateoasRepository;

@RestController
@RequestMapping(path="/paish", produces = "application/hal+json")
public class PaisHateoasController  {
	@Autowired
	private PaisHateoasRepository paisHateoasRepository;
	
	/**
	 * Obtiene todos los paises: GET http://{servidor}:{puerto}/paish
	 * @return Lista de paises
	 */
	@RequestMapping(method = RequestMethod.GET)
	public Iterable<PaisHateoas> findAll() {
		return paisHateoasRepository.findAll() ;
	}
	
	/**
	 * Obtiene un pais por id: GET http://{servidor}:{puerto}/paish/{id}
	 * @param id Id del pais. En la Url
	 * @return Un pais
	 */
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public PaisHateoas unPais(@PathVariable int id) {
		return paisHateoasRepository.unPais(id);
	}
}

Explicación de PersonaHateoasController

Todas las demás clases ya las conocemos, solo vamos a explicar algunos detalles de la clase PersonaHateoasController.

  1. Hateoas (Hypermedia As The Engine Of Application State). Aunque en nuestros ejemplos estamos modelando tablas de una Base de Datos, normalmente conocido como CRUD (Create Read Update and Delete),  lo que estamos haciendo es exponer  la capa de datos y dejando que el consumidor interprete e implemente la lógica del negocio. Siguiendo la filosofía de REST deberíamos de exponer los distintos casos de uso del sistema y no solo una simple interfaz. La intención de Hateoas es alejarse de CRUD y basarse en un enfoque hypermedia.
  2. application/hal+json. Existen diferentes formas de exponer los recursos en hypermedia, uno de los más extendidos es HAL (Hypertext Application Language). Es una extensión de json con la capacidad de definir enlaces y controles. application/hal+json es el tipo mime para HAL.
  3. Método personaToResource con parámetro PersonaHateoas. Crea los link y los retorna como un Resource (Recurso).
  4. Método personaToResource con parámetro List<PersonaHateoas>.  Recorre la lista de PersonaHateoas para cargar los Recursos.

Resultado

Al ejecutar

http://localhost:8080/personah

El resultado es:

{
  "_embedded": {
    "personaHateoasList": [
    {
      "idPersona": 1,
      "idGrupo": 1,
      "idPais": 2,
      "edad": 2,
      "nombre": "Juan",
      "sexo": "M",
      "_links": {
        "self": {
          "href": "http://localhost:8080/personah/1"
        },
        "Grupo": {
          "href": "http://localhost:8080/grupoh/1"
        },
        "Pais": {
          "href": "http://localhost:8080/paish/2"
        }
     }
  },
  {
    "idPersona": 2,
    "idGrupo": 2,
    "idPais": 3,
    "edad": 10,
    "nombre": "Linda",
    "sexo": "F",
    "_links": {
      "self": {
        "href": "http://localhost:8080/personah/2"
      },
      "Grupo": {
        "href": "http://localhost:8080/grupoh/2"
      },
      "Pais": {
        "href": "http://localhost:8080/paish/3"
      }
    }
 },
 {
   "idPersona": 3,
   "idGrupo": 2,
   "idPais": 1,
   "edad": 5,
   "nombre": "Luisa",
   "sexo": "M",
   "_links": {
     "self": {
       "href": "http://localhost:8080/personah/3"
     },
     "Grupo": {
       "href": "http://localhost:8080/grupoh/2"
     },
     "Pais": {
       "href": "http://localhost:8080/paish/1"
     }
   }
 }
]
},
"_links": {
  "self": {
    "href": "http://localhost:8080/personah"
  }
 }
}

Otros

Descargar proyecto

Se puede descargar el proyecto completo de https://github.com/arielolivagh/administrarPersona/releases/tag/v2.0

API REST 4. Calidad al implementar aplicacion Web

Introducción

Existen 3 niveles para aplicar API REST en el desarrollo de una Aplicación Web.
Cumplir con estás nos garantiza las mejores practicas para su implementación, integración, estabilidad y escabilidad.
Para detalles se puede consultar  http://asiermarques.com/2013/conceptos-sobre-apis-rest/

Nivel 1. Uso correcto de URIs

  1. Identificar de forma única el recurso
  2. Las URI no deben contener acciones
  3. Independientes del formato
  4. Jerarquía lógica
  5. Los filtrados no se deben hacer en la URI

Nivel 2. HTTP

  1. Usar métodos HTTP; GET, POST, PUT, DELETE y PATCH
  2. Código de estado
  3. Aceptación de tipos de contenido

Nivel 3. Hypermedia

Se vera mas a detalle posteriormente.

Con lo que llevamos hasta este momento, podemos generar un proyecto de API REST en Web con las mejores practicas.
Vamos a retomar la tabla Persona y crear un Sistema Web con API REST para administrar personas; crear, leer, actualizar y eliminar.

Construir aplicación

Crear proyecto

Ir a

File -> New -> Other… -> Spring Boot -> Spring Starter Project

Seleccionamos nombre del proyecto, tipo, versión de java, paquete, entre otras opciones. Clic en Next>. Seleccionamos la versión de Spring Boot y las dependencias:

  1. JPA
  2. H2
  3. MySQL
  4. Web

Clic en Next> y posteriormente clic en Finish.

Crear paquetes

Creamos los paquetes

  • com.mio.administrar.controller
  • com.mio.administrar.repository
  • com.mio.administrar.entity

Copiar clases e interfaces

Tomando las clases e interfaces del proyecto anterior https://github.com/arielolivagh/gs-rest-service-complete/tree/v2.0  copiamos:

  • La clase PersonaController al paquete com.mio.administrar.controller
  • La interface PersonaRepository al paquete com.mio.administrar.repository
  • La clase Persona al paquete com.mio.administrar.entity

Conexión a la Base de Datos

Escribimos los parámetros de conexión a la Base de Datos en el archivo application.properties.

spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/Pruebas
spring.datasource.username=root
spring.datasource.password=root

Crear consulta para búsqueda de personas por id

En la clase repository vamos a crear un nuevo query para obtener una persona por su id.

@Query("select p from Persona p where p.idPersona = ?1")
Persona findPersona(int id);

Crear controller de calidad

Crear personas

  • Método POST
  • La respuesta es Json
  • Documentación JavaDoc
    /**
     * Crea una persona: POST http://{servidor}:{puerto}/persona
     * @param nombre Nombre de la persona
     * @param edad Edad de la persona
     * @param sexo Sexo de la persona
     * @return mensaje de 'Agregado correctamente'
     */
    @RequestMapping(method=RequestMethod.POST,produces={"application/json"})
    public String createPersona(@RequestParam String nombre,@RequestParam int edad,@RequestParam String sexo) {
            Persona p = new Persona();
    	p.setNombre(nombre);
    	p.setEdad(edad);
    	p.setSexo(sexo);
    	personaRepository.save(p);
    		
            return "Agregado correctamente";
    }

    Consumir:

    curl -i -X POST -d "nombre=Mariel&edad=51&sexo=F" http://localhost:8080/persona

Leer todas las personas

  • Método GET
  • La respuesta es Json
  • ocumentación JavaDoc
    /**
     * Obtiene todas las personas: GET http://{servidor}:{puerto}/persona
     * @return Lista de personas
     */
     @RequestMapping(method=RequestMethod.GET,produces={"application/json"})
        public Iterable<Persona> readPersonas() {
        return personaRepository.findAll();
     }

    Consumir:

    curl -i -X GET http://localhost:8080/persona

Leer una persona

  • Método GET
  • La respuesta es Json
  • Documentación JavaDoc
    /**
     * Obtiene una person por id: GET http://{servidor}:{puerto}/persona/{id}
     * @param id Id de la persona. En la Url
     * @return Una personas
     */
     @RequestMapping(value="/{id}",method=RequestMethod.GET,produces={"application/json"})
        public Persona readPersonaById(@PathVariable("id") int id) {
        return personaRepository.findPersona(id);
     }

    Consumir:

    curl -i -X GET http://localhost:8080/persona/7

Actualiza una persona

  • Método PUT
  • La respuesta es Json
  • Documentación JavaDoc
    /**
     * Actualiza una persona por Id: PUT http://{servidor}:{puerto}/persona/{id}
     * @param id Id de la persona. En la Url
     * @param nombre Nombre de la persona
     * @param edad Edad de la persona
     * @param sexo Sexo de la person
     * @return mensaje de 'Actualizado correctamente'
     */
     @RequestMapping(value="/{id}",method=RequestMethod.PUT,produces={"application/json"})
     public String updatePersona(@RequestParam String nombre,@RequestParam int edad,@RequestParam String sexo,@PathVariable("id") int id) {
        Persona p = new Persona();
        p.setIdPersona(id);
        p.setNombre(nombre);
        p.setEdad(edad);
        p.setSexo(sexo);
        personaRepository.save(p);
        return "Actualizado correctamente";
     }

    Consumir:

    curl -i -X PUT -d "nombre=Ariel&edad=100&sexo=M" http://localhost:8080/persona/7

Actualiza edad de una persona

  • Método PATCH
  • La respuesta es Json
  • Documentación JavaDoc
    /**
     * Actualiza la edad de una persona por Id: PATCH http://{servidor}:{puerto}/persona/{id}
     * @param id Id de la persona. En la Url
     * @param edad Edad de la persona
     * @return mensaje de 'Actualizando la edad correctamente'
     */
     @RequestMapping(value="/{id}",method=RequestMethod.PATCH,produces={"application/json"})
     public String updateParcialPersona(@PathVariable("id") int id,@RequestParam int edad) {
        Persona p = personaRepository.findPersona(id);
        p.setEdad(edad);
        personaRepository.save(p);
        return "Actualizando la edad correctamente";
     }

    Consumir:

    curl -i -X PATCH -d "edad=10" http://localhost:8080/persona/7

Elimina todas las personas

  • Método DELETE
  • La respuesta es Json
  • Documentación JavaDoc
    /**
     * Elimina todas las personas: DELETE http://{servidor}:{puerto}/persona
     * @return mensaje de 'Todo eliminado correctamente'
     */
     @RequestMapping(method=RequestMethod.DELETE,produces={"application/json"})
        public String deleteAllPersona() {
        personaRepository.deleteAll();
        return "Todo eliminado correctamente";
     }

    Consumir:

    curl -i -X DELETE http://localhost:8080/persona

Elimina una persona

  • Método DELETE
  • La respuesta es Json
  • Documentación JavaDoc
    /**
     * Elimina una persona por id: DELETE http://{servidor}:{puerto}/persona/{id}
     * @param id Id de la persona. En la Url
     * @return mensaje de 'Eliminado correctamente'
     */
     @RequestMapping(value="/{id}", method=RequestMethod.DELETE,produces={"application/json"})
     public String deletePersona(@PathVariable("id") int id) {
        Persona p = new Persona();
        p.setIdPersona(id);
        personaRepository.delete(p);
        return "Eliminado correctamente";
     }

    Consumir:

    curl -i -X DELETE http://localhost:8080/persona/7

Otras

Descargar proyecto

Se puede descargar el proyecto completo de https://github.com/arielolivagh/administrarPersona/tree/v1.0

API REST 3. Publicar y consumir

Descripción

Publicar y consumir algunos métodos creados del API REST.

Publicar y Consumir

Usando CRUD

Simplemente usando los métodos que ya nos proporciona el CRUD

Crear Persona

Insertamos un nuevo registro a la tabla Persona.

  1. En la clase PersonaController
  2. Se mapea con /add
  3. Recibe los parámetros nombre, edad y sexo
  4. Se carga el Objeto Persona y se ejecuta la función save().
  5. Retorna el mensaje Agregado correctamente.
    @RequestMapping("/add")
    public String addPersona(@RequestParam  String nombre,@RequestParam  int edad,@RequestParam  String sexo) {
       Persona p = new Persona();
       p.setNombre(nombre);
       p.setEdad(edad);
       p.setSexo(sexo);
       personaRepository.save(p);
    		
       return "Agregado correctamente";
    }

     

Resultado

Consultar todas las Personas

Consultamos todos los registros de la tabla Persona.

  1. En la clase PersonaController
  2. Se mapea con /all
  3. No recibe parámetros
  4. Retorna el resultado de la función findAll().
    @RequestMapping("/all")
       public Iterable<Persona> getPersona() {
       return personaRepository.findAll();
    }

Resultado

Borra una Persona

Borramos un registro de la tabla Persona.

  1. En la clase PersonaController
  2. Se mapea con /delete
  3. Recibe el parámetro id
  4. Se carga el Objeto Persona y se ejecuta la función delete().
  5. Retorna el mensaje Borrado correctamente.
    @RequestMapping("/delete")
     public String deletePersona(@RequestParam int id) {
     Persona p = new Persona();
     p.setIdPersona(id);
     personaRepository.delete(p);
     return "Borrado correctamente";
     }
    

Resultado

Total de Personas

Total de registros de la tabla Persona.

  1. En la clase PersonaController
  2. Se mapea con /total
  3. No recibe parámetros
  4. Retorna el resultado de la función count().
    @RequestMapping("/total")
       public long totalPersona() { 
       return personaRepository.count();
    }

Resultado

Creamos consultas propias

Podemos crear otras consultas de acuerdo a nuestras necesidades

Persona por tipo de sexo

Obtenemos las Personas por su tipo de sexo

  1. En la interface PersonaRepository agregamos la consulta que filtra por tipo de sexo
    @Query("select p from Persona p where p.sexo = ?1")
    Iterable<Persona> findBySexo(String sexo);
  2. En la clase PersonaController
  3. Se mapea con /allBySexo
  4. Recibe el parámetro sexo
  5. Retorna el resultado de la función findBySexo().
    @RequestMapping("/allBySexo")
    public Iterable<Persona> getPersonaBySexo(@RequestParam String sexo) {
       return personaRepository.findBySexo(sexo);
    }

Resultado

Persona por tipo de sexo con paginado

Obtenemos las Personas por su tipo de sexo. Los resultados se muestran paginados.

  1. En la interface PersonaRepository agregamos la consulta que filtra por tipo de sexo y agrega paginado.
    @Query("select p from Persona p where p.sexo = ?1")
    Page<Iterable<Persona>> findBySexoPag(@Param("sexo") String sexo,Pageable pageable);
  2. En la clase PersonaController
  3. Se mapea con /sexoPag
  4. Recibe los parámetros sexo, inicio y bloque. Estos 2 últimos parámetros para el paginado.
  5. Se pasa los parámetros de paginado a la función findBySexoPag()
  6. Retorna el resultado de la función findBySexoPag().
    @RequestMapping(value="/sexoPag")
       public Page<Iterable<Persona>> personaPag(@RequestParam String sexo,@RequestParam int inicio,@RequestParam int bloque) {
       return personaRepository.findBySexoPag(sexo, new PageRequest(inicio, bloque));
    }

Resultado

Persona por tipo de sexo con paginado y ordenado por un campo

Obtenemos las Personas por su tipo de sexo. Los resultados se muestran paginados y ordenados por un campo.

  1. En la clase PersonaController
  2. Se mapea con /sexoOrder
  3. Recibe los parámetros sexo, inicio, bloque y orden.
  4. Se pasa los parámetros de paginado y orden a la función findBySexoPag()
  5. Retorna el resultado de la función findBySexoPag().
    @RequestMapping(value="/sexoOrder")
       public Page<Iterable<Persona>> personaOrder(@RequestParam String sexo,@RequestParam int inicio,@RequestParam int bloque,@RequestParam String orden) {
       return personaRepository.findBySexoPag(sexo, new PageRequest(inicio, bloque,Sort.Direction.ASC, orden));
    }

Resultado

Otras opciones

Podemos crear otras consultas con otros métodos HTTP; POST, PUT, DELETE  o PATCH. También enviando HEAD en la petición.

Agregar Persona con el método POST

@RequestMapping(value="/add",method=RequestMethod.POST)
   public String addPersonaPost(@RequestParam  String nombre,@RequestParam  int edad,@RequestParam  String sexo) {
   Persona p = new Persona();
   p.setNombre(nombre);
   p.setEdad(edad);
   p.setSexo(sexo);
   personaRepository.save(p);
   return "Agregado correctamente";
}

Resultado. Utilizando curl:

Obtener Personas del sexo=M mediante HEAD

@RequestMapping(value="/sexo",headers={"sexo=M"})
   public Iterable<Persona> personaM() {
   return personaRepository.findBySexo("M");
}

Resultado. Utilizando curl:

Otros

Descargar Proyecto

Se puede descargar el proyecto completo de https://github.com/arielolivagh/gs-rest-service-complete/tree/v2.0

API REST 2. Conectarse a Base de Datos MySQL

Descripción

Para publicar y consumir algunos métodos del API REST vamos a crear y conectarnos a una Base de Datos MySQL.

Crear la Base de Datos

Con el siguiente script podemos crear la Base de Datos en MySQL.

-- MySQL dump 10.13  Distrib 5.7.19, for Linux (x86_64)
--
-- Host: localhost    Database: Pruebas
-- ------------------------------------------------------
-- Server version	5.7.19-0ubuntu0.16.04.1

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `Persona`
--

DROP TABLE IF EXISTS `persona`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `persona` (
  `idPersona` int(11) NOT NULL AUTO_INCREMENT,
  `nombre` varchar(200) NOT NULL,
  `edad` int(11) NOT NULL,
  `sexo` char(1) NOT NULL,
  PRIMARY KEY (`idPersona`),
  UNIQUE KEY `idPersona_UNIQUE` (`idPersona`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `Persona`
--
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2017-08-29 12:27:04

Agregar dependencias

Tomamos el proyecto http://arieloliva.com/api-rest-en-spring/.

En el archivo pom.xml agregamos las dependencias necesarias:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
</dependency>

Crear archivo Application.properties

En la ruta src/main/resources del proyecto creamos el archivo application.properties. Agregamos los siguientes parámetros de la conexión a la Base de Datos.

spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/Pruebas
spring.datasource.username=root
spring.datasource.password=root

Crear entity

Un entity es el modelado de una tabla. Nuestra tabla se llama Persona y tiene los campos:

Nombre Tipo Tamaño Null AutoIncrementar
idPersona int 11 no si
nombre varchar 200 no no
edad int 11 no no
sexo char 1 no no

Con estos datos podemos crear la Clase que modela la tabla.

  1. Crear Clase en el paquete hello con nombre Persona
  2. variables privadas idPersona, nombre, edad y sexo con sus respectivos set y get
  3. Anotación @Entity en la Clase para indicar que es un entity.
  4. Anotación @Id y @GeneratedValue(strategy=GenerationType.IDENTITY) en la variable idPersona. para indicar que es el identificador único y se auto genera.

El resultado final es:

package hello;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Persona implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int idPersona;

	private int edad;
	private String nombre;
	private String sexo;

	public Persona() {
	}
	public int getIdPersona() {
		return this.idPersona;
	}
	public void setIdPersona(int idPersona) {
		this.idPersona = idPersona;
	}
	public int getEdad() {
		return this.edad;
	}
	public void setEdad(int edad) {
		this.edad = edad;
	}
	public String getNombre() {
		return this.nombre;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	public String getSexo() {
		return this.sexo;
	}
	public void setSexo(String sexo) {
		this.sexo = sexo;
	}
}

Crear repositorio

Un repositorio es una interface con los métodos necesarios para acceso a la Base de Datos.

  1. Crear Interface en el paquete hello con nombre PersonaRepository
  2. Extendemos de CrudRepository<Persona, Long>. Crud (Create, Read, Update, Delete). Proporciona todos los métodos para Crear, Leer, Actualizar y Borrar los datos de la tabla.

El resultado final es:

package hello;

import org.springframework.data.repository.CrudRepository;

public interface PersonaRepository extends CrudRepository<Persona, Long>{
		
}

Crear controlador

  1. Crear Clase en el paquete hello con nombre PersonaController
  2. Anotaciones @RestController y @RequestMapping(path=»/persona») en la Clase. Para indicar que que es una API REST y la base del mapeo es /persona.
  3. Variable private PersonaRepository personaRepository con anotación @Autowired la cual obtiene el llamado a PersonaRepository.
  4. Método public Iterable<Persona> getPersona() con la anotación @RequestMapping(«/all») para obtener todos los datos de la tabla persona con el mapeo all.

El resultado final es:

package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path="/persona")
public class PersonaController {
	@Autowired
	private PersonaRepository personaRepository;
	
	@RequestMapping("/all")
	public Iterable<Persona> getPersona() {
        return personaRepository.findAll();
    }
}

Ejecutar aplicación

Después de ejecutar la aplicación el resultado obtenido es:

Otros

Descargar proyecto

Se puede descargar el proyecto completo de https://github.com/arielolivagh/gs-rest-service-complete/tree/v1.0

API REST 1. Spring

Descripción

Crear un API REST en Spring en sumamente sencillo. Vamos a crear uno utilizando lo siguiente:

  1. Eclipse
  2. Librerías de Spring
  3. Terminal
  4. Java

Pasos

Los pasos se detallan a continuación:

Instalar eclipse

  1. Descargar eclipse http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/neon3
  2. Descomprimir en alguna ubicación.
  3. Ejecutar y seleccionar carpeta de trabajo (workspace) .

Instalar librerías de Spring

En la pagina https://spring.io/tools/sts/all de Spring nos muestra el repositorio para instalar STS (Spring Tool Suite).

  1. En eclipse ir a Help -> Install New Software… -> Add…
  2. En la ventana que se muestra escribir STS en name y http://dist.springsource.com/release/TOOLS/update/e4.7/ en Location. Y clic en OK.
  3. Seleccione todas las opciones y clic en Next.
  4. Aceptar licencia, esperar a que termine de instalarse y reiniciar eclipse.

Importar proyecto Spring

Con la instalación de STS podemos integrar proyectos en Spring ya terminados.

  1.  Ir a File -> New -> Other… -> Spring Boot -> Import Spring Getting Started Content. Clic en Next.
  2. Seleccionar Rest Service. Maven en la sección Build Type, complete en la sección Code Sets y clic en Finish.
  3. Se crear el proyecto y se abre una pagina de ayuda.

Crear jar del proyecto

  1. Abrir la terminal de eclipse o del Sistema Operativo
  2. Pasarse a la ubicación del proyecto
    cd .../developer_spring/ide/workspace/gs-rest-service-complete
  3. Ejecutar
    ./mvnw clean package
  4. En la terminal se muestra que la construcción fue correcta.

Ejecutar proyecto

  1. Abrir la terminal de eclipse o la terminal del Sistema Operativo.
  2. Pasarse a la ubicación del jar recién creado.
    cd .../developer_spring/ide/workspace/gs-rest-service-complete/target/
  3. Ejecutar jar.
    java -jar gs-rest-service-0.1.0.jar
  4. Inicia la carga de Spring
  5. El mensaje Started Application in … indica que se ha levantado correctamente.

Ver resultado

  1. Abrir explorador de Internet
  2. Capturar la URL http://localhost:8080/greeting
  3. O http://localhost:8080/greeting?name=Ariel