miércoles, 8 de julio de 2020

CRUD con RESTful API y JSON

Las aplicaciones de diversos sistemas en diversos casos no pueden acceder directamente a servidores de bases de datos por diversos motivos como las aplicaciones de Android, estas pueden tener sus bases de datos locales como SQLite, pero si desean interactuar con una base de datos externa como un MySQL requieren un puente que les permita ducha conexión. En este entorno aparecen los Servicios Web, que tienen muchas más aplicaciones y funcionalidades, que permitirán a las aplicaciones acceder a esas bases de datos y realizar operaciones CRUD.

Para crear un WebService se siguen los pasos comunes en su construcción (Clic aquí para ver como crear un WebService desde cero). El siguiente paso es crear la conexión a la base de datos.

Para poder ejecutar las operaciones de CRUD mediante un Servicio Web REST con java debemos definir una conexión mediante una clase Java (clic aquí para ver la conexión de Java a MySQL) o mediante JPA


Código

NOTA: En este ejemplo no se usará la unidad de persistencia (persistence.xml).

Teniendo como base a la tabla "Inquilinos"(clic aquí para ver el DTO) a la que le realizaremos un mantenimiento (CRUD) teniendo como DAO a "DaoInquilinos" y su correspondiente implementación (clic aquí para ver el DAO).Además, las pruebas a las funciones declaradas en el DAO (clic aquí para ver el DAO).

Se creará un punto de partida para el WebService llamado RestApi que tendrá el siguiente código

package apiRest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/service")
public class RestApi extends Application {

}

Luego debemos de crear las funciones REST que se emplearán para las diversas funcionalidades del CRUD, como vamos a emplear JSON se puede optar por usar alguna biblioteca para manipular este formato de intercambio, pero se puede realizar este mismo procedimiento sin agregar ninguna bilbioteca adicional a Jersey y JAX-RS 2.0. Tendríamos una clase llamada JSONRest

package apiRest.services;

import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/inquilino")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class JSONRest {
    /* Dentro se colocan los métodos 
    	-GET
        -POST
        -PUT
        -DELETE
    */    
}

SELECT

Tendremos dos casos, cuando recibimos todos los registros(inquilinosSEL) y cuando se recibe solo uno de los registros(inquilinosGET)

La clase JSONRest contiene el siguiente código

@GET
@Path("/{id}")
public Response inquilinosGET(@PathParam("id") int id) {
    DaoInquilinos dao = new DaoInquilinosImpl();
    Inquilinos inquilino=  dao.inquilinosGet(id);
    if (inquilino == null) {
        String msg = "Inquilino no encontrado con id: " + id;
        return Response.ok(msg, MediaType.TEXT_PLAIN).build();
    } else {
        return Response.ok(inquilino).build();
    }
}

@GET
public Response inquilinosSEL() {
    DaoInquilinos dao = new DaoInquilinosImpl();
    List<Inquilinos> lista = dao.inquilinosSel();
    if (lista == null) {
        String msg = "Sin usuarios";
        return Response.ok(msg, MediaType.TEXT_PLAIN).build();
    } else {
        GenericEntity<List<Inquilinos>> entity
                = new GenericEntity<List<Inquilinos>>(lista) {
        };
        return Response.ok(entity).build();
    }
}

Probando desde el navegador

  • http://localhost:8082/WebService/service/inquilinos/1
  • http://localhost:8082/WebService/service/inquilinos/

Nota: la dirección (localhost) y el puerto (8082) variarán dependiendo de la ubicación y puerto empleado por el servidor de Java EE.

En el caso de la función "inquilinoGET" que requiere un valor para hacer la búsqueda. Otra forma es usar la anotación que es @QueryParam en lugar de @PathParam y esa anotación a su vez tiene como parámetro el nombre que tendrá desde la petición, similar a:
http://localhost:8082/WebService/service/inquilino/?id=1

INSERT

Se emplea el método POST, indicado mediante una anotación, para enviar la información a insertar, en nuestro caso es la BD la que genera automáticamente el código

La clase JSONRest contiene el siguiente código

@POST
@Produces(MediaType.TEXT_PLAIN)
public Response inquilinosINS(Inquilinos inquilino) {
    DaoInquilinos dao = new DaoInquilinosImpl();
    String msg = dao.inquilinosIns(inquilino);
    return Response.ok(msg).build();
}

UPDATE

Se emplea el método PUT, indicado mediante una anotación, para enviar la información que se desea modificar.

La clase JSONRest contiene el siguiente código

@PUT
@Produces(MediaType.TEXT_PLAIN)
public Response inquilinosUPD(Inquilinos inquilino) {
    DaoInquilinos dao = new DaoInquilinosImpl();
    String msg = dao.inquilinosUpd(inquilino);
    return Response.ok(msg).build();
}

DELETE

Urilizando la anotación DELETE se envía los identificadores de los registros a eliminar separados por comas. También se puede relizar eliminaciones individuales

La clase JSONRest contiene el siguiente código

@DELETE
@Path("/{ids}")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public Response inquilinosDEL(@PathParam("ids") String ids) {
    List<Integer> list = Arrays.stream(ids.split(","))
            .map(Integer::parseInt)
            .collect(Collectors.toList());
    DaoInquilinos dao = new DaoInquilinosImpl();
    String msg = dao.inquilinosDel(list);
    return Response.ok(msg).build();
}

Código completo

package apiRest.services;

import dao.DaoInquilinos;
import dao.impl.DaoInquilinosImpl;
import dto.Inquilinos;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/inquilinos")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class JSONRest {

    @GET
    @Path("/{id}")
    public Response inquilinosGET(@PathParam("id") int id) {
        DaoInquilinos dao = new DaoInquilinosImpl();
        Inquilinos inquilino = dao.inquilinosGet(id);
        if (inquilino == null) {
            String msg = "Inquilino no encontrado con id: " + id;
            return Response.ok(msg, MediaType.TEXT_PLAIN).build();
        } else {
            return Response.ok(inquilino).build();
        }
    }

    @GET
    public Response inquilinosSEL() {
        DaoInquilinos dao = new DaoInquilinosImpl();
        List<Inquilinos> lista = dao.inquilinosSel();
        if (lista == null) {
            String msg = "Sin usuarios";
            return Response.ok(msg, MediaType.TEXT_PLAIN).build();
        } else {
            GenericEntity<List<Inquilinos>> entity
                    = new GenericEntity<List<Inquilinos>>(lista) {
            };
            return Response.ok(entity).build();
        }
    }

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    public Response inquilinosINS(Inquilinos inquilino) {
        DaoInquilinos dao = new DaoInquilinosImpl();
        String msg = dao.inquilinosIns(inquilino);
        return Response.ok(msg).build();
    }

    @PUT
    @Produces(MediaType.TEXT_PLAIN)
    public Response inquilinosUPD(Inquilinos inquilino) {
        DaoInquilinos dao = new DaoInquilinosImpl();
        String msg = dao.inquilinosUpd(inquilino);
        return Response.ok(msg).build();
    }

    @DELETE
    @Path("/{ids}")
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    public Response inquilinosDEL(@PathParam("ids") String ids) {
        List<Integer> list = Arrays.stream(ids.split(","))
                .map(Integer::parseInt)
                .collect(Collectors.toList());
        DaoInquilinos dao = new DaoInquilinosImpl();
        String msg = dao.inquilinosDel(list);
        return Response.ok(msg).build();
    }
}

Se puede apreciar en este caso que en la anotación @Produces y @Consumes se indica explícitamente que se trabajará con JSON, pero en las respuestas de las funciones el tipo de retorno no es String, sino "Response", esto permite formatear correctamente el contenido capturado por las funciones DAO y transformarlo a JSON sin emplear otra biblioteca.

2 comentarios: