RESTful parte 4: Actualizando y eliminando elementos de una colección.
Todo mantenimiento de objetos debe tener siempre lo que en inglés se llama CRUD (Create - Read - Update - Delete). Hasta ahora hemos visto C y R. Faltan el U y el D. Así que en este artículo hablaremos de ello
En cualquier manera de almacenamiento de datos (sea un arreglo o una base de datos) siempre se debe poder identificar a un solo objeto a través de una clave principal. ¿Cómo se puede borrar o actualizar un solo objeto si no sabemos cuál es?.
En nuestros anteriores artículos hemos creado objetos y le hemos puesto un ID automático. Aunque no es la manera más optima de hacerlo (ya que le hemos puesto como ID el tamaño del arreglo), vamos a actualizar un poco esta asignación. Para ello editaremos la clase
Persona
de tal manera que tenga un contador de objetos. Para ello hagamos lo siguiente.//... public class Persona { private static int contador = 0; public static int getNuevoId() { return ++contador; } //...
Luego, en la clase
PersonasResource
, en el método guardar
le cambiaremos la línea donde se asigna el ID con lo siguiente://... public Response guardar(Persona p) { p.setIdPersona(Persona.getNuevoId()); //nueva manera de asignar ID personas.add(p); return Response.ok(p).build(); } //...
Ahora, necesitamos también una manera óptima de identificar un objeto
Persona
a través de su ID. Creame que usando el for()
no es la manera óptima. Debemos usar los mecanismos propios de Java. Para ello, debemos reescribir el método equals()
en la clase Persona
. Y, como siempre, el NetBeans nos va a ayudar. Abramos esa clase y hagamos clic derecho y seleccionamos Insert code...
y luego seleccionamos "equals() & hashCode()...". Ahora, seleccionamos el campo "idPersona" en ambos paneles. Esto es que se utilizará el campo "idPersona" para hacer la comparación (equals) y para agrupar la comparación (hashCode)
Clic en "Generate".
Y vemos que el NetBeans creó los métodos mencionados.
Una rápida explicación de esos métodos: el equals() sirve para poder comparar dos objetos. Es la única manera de comparar objetos que utilizará el Java. Así nos evitaremos hacer una comparación de un campo por nuestra cuenta cada vez que necesitemos. Y método hashCode() permite devolver un valor que permitirá reducir la comparación. Por ejemplo, si tenemos mil objetos, y todos tienen id diferentes, y necesitamos buscar un ID específico, el Java agrupará todos los que tengan el hashCode similar, y después hará la búsqueda uno por uno dentro de ese grupo reducido.
Obtener un objeto de la colección a través de su ID
Lo que haremos es acceder a un objeto de la colección pero le damos el ID como parte del URL. Por ejemplo, hasta el momento hemos accedido a un URL como este:http://localhost:8080/PersonaRESTWeb/resources/listaPersonas
... y nuestro reto ahora es obtener el objeto con ID=2 usando este URL
http://localhost:8080/PersonaRESTWeb/resources/listaPersonas/2/
Antes de continuar, recordemos en el artículo donde se habló cómo manejar un solo objeto en RESTful, que el recurso mismo es el que tiene un URL para devolver el objeto.Entonces, modificaremos el recurso
PersonasResource
(el que tiene el arreglo) para que devuelva el PersonaResource
(el que tiene un solo objeto) y devuelva el objeto seleccionado. Entonces, debemos modificar este último recurso para que la variable Persona
que tiene no sea static
, pero que el valor de esa variable sea recibida por un método.//... public class PersonaResource { private Persona persona; //... public void setPersona(Persona persona) { this.persona = persona; } }
Ahora sí, regresemos al recurso
PersonasResource
(el que tiene la colección de objetos) y agreguemos una referencia al recurso PersonaResource
. Como es un EJB, entonces lo declaramos como tal://... @Stateless @Path("/listaPersonas") public class PersonasResource { @EJB PersonaResource personaResource; //...
Luego, agregamos un método que recibirá como parámetro el ID del objeto a buscar, busca el ID en el arreglo, y si existe, le asignará al recurso
personaResource
y lo devolverá al cliente.//... public PersonaResource getPersona(Integer id) { if (id == null) { //si no se pasó el ID... return null; //... termina el método } Persona $temp = new Persona(); //creamos un temporal... $temp.setIdPersona(id); //.. que tendrá el ID... if (personas.contains($temp)) { // ... para buscarlo en la colección. Para eso sirve el método equals(). Si existe en la colección... int idx = personas.indexOf($temp); //... obtenemos su índice... Persona actual = personas.get(idx); //... lo obtenemos de la colección... personaResource.setPersona(actual); //.. y lo marcamos como "actual"... return personaResource; //... para que lo devuelva al cliente. } return null; //.. si no lo encuentra, devuelve null } //...Listo, y funciona... pero no tan rápido ¿Cómo sabemos que el ID será parte del URL?. Bien, agregaremos la anotación
@Path("{idPersona}/")
de la siguiente manera//... @Path("{idPersona}/") public PersonaResource getPersona(Integer id) { //...... y para asociar ese parámetro al parámetro del método, lo hacemos con la anotación
@PathParam()
de la siguiente manera://... @Path("{idPersona}/") public PersonaResource getPersona( @PathParam("idPersona") Integer id) { //...
Al final, el método completo es como sigue:
//... @Path("{idPersona}/") public PersonaResource getPersona(@PathParam("idPersona") Integer id) { if (id == null) { //si no se pasó el ID... return null; //... termina el método } Persona $temp = new Persona(); //creamos un temporal... $temp.setIdPersona(id); //.. que tendrá el ID... if (personas.contains($temp)) { // ... para buscarlo en la colección. Para eso sirve el método equals(). Si existe en la colección... int idx = personas.indexOf($temp); //... obtenemos su índice... Persona actual = personas.get(idx); //... lo obtenemos de la colección... personaResource.setPersona(actual); //.. y lo marcamos como "actual"... return personaResource; //... para que lo devuelva al cliente. } return null; //.. si no lo encuentra, devuelve null } //...
Listo, ahore probemos colocando unos valores como:
{"nombre_persona":"Albert"} {"nombre_persona":"Bernard"} {"nombre_persona":"Carl"}
... desde el Test de NetBeans.
Ahora, del árbol izquierdo, abramos el nodo "listaPersonas" y seleccionemos el nodo "{idPersona}"
Y en el campo "idPersona", escribiremos el ID de uno de los creados, por ejemplo "2", y hacemos clic en "Test"
También podemos probar desde el URL de la siguiente manera. Primero toda la lista.
http://localhost:8080/PersonaRESTWeb/resources/listaPersonas
Y luego, el del ID=2
http://localhost:8080/PersonaRESTWeb/resources/listaPersonas/2/
Reemplazar objeto y borrar objeto de la colección
El recursoPersonasResource
es el que tiene la colección de objetos. Así que aquí le deberemos agregar los métodos que modifiquen y eliminen los objetos de la colección. Pero - OJO - estos no serán accedidos desde el URL. Solo fueron puestos aquí porque tiene la colección. Si se manejara una base de datos, se tendría otro EJB que maneje esos objetos. Bien, nuestro recurso tendrá los siguientes métodos://... public void borrarPersona(Persona p) { personas.remove(p); //... busca en la lista y borra el elemento } public void cambiarPersona(Persona actual, Persona p) { int idx = personas.indexOf(actual); //... obtiene la posición del actual if (idx >= 0) { //... si existe... p.setIdPersona(actual.getIdPersona()); //... ponerle el ID en el nuevo objeto... personas.set(idx, p); //..y reemplazar el objeto en la misma posición del anterior } }//...Cada línea está comentada para que quede bien explicado.
Recordemos otra vez que nuestro recurso
PersonaResource
tendrá el objeto seleccionado por el ID. Y como el que tiene la colección de objetos es el recurso PersonasResource
y es un EJB, entonces agregaremos la referencia de la siguiente manera://... public class PersonaResource { //el que tiene el objeto actual @EJB PersonasResource personasResource; //el que tiene la colección de objetos //...
Entonces los métodos
@PUT
y @DELETE
será sobre el objeto seleccionado. Así que en este recurso agregaremos los siguientes métodos://... @DELETE public void borrar() { personasResource.borrarPersona(persona); //porque tiene la colección de objetos } @PUT @Consumes({"application/xml", "application/json"}) public void actualizar(Persona p) { personasResource.cambiarPersona(persona,p); //porque tiene la colección de objetos } //...
Y listo. Probemos el test de NetBeans,´agregamos los mismo objetos, y probemos la actualización: Seleccionamos el idPersona:2 y le ponemos el nuevo objeto a reemplazar, utilizando el método "PUT"
Y cuando consultamos el ID=2, este será el nuevo objeto:
Y luego para el método "DELETE", y le indicamos el de ID=1
.. y luego obtenemos el listado de objetos.
Código fuente del proyecto
Como siempre, aquí publico el código fuente del proyecto utilizado en este artículo para que lo prueben y vean que no miento:)
http://kenai.com/projects/apuntes/downloads/download/CRUDPersonasRest%252FPersonaRESTWeb.tar.gz
Muchisimas gracias por el trabajo.
ResponderBorrarHola Diego, antes que nada se te agradece por estas entradas, soy muy importantes para los que desconocemos de algún tema.
ResponderBorrarQuisiera saber porque me manda este error al probar mis Restful?
type : Status report
message : Bad Request
description : The request sent by the client was syntactically incorrect (Bad Request).
esto sucede en el siguiente caso, primero pruebo el get, por obvias razones no me trae nada después ejecuto el Post en cualquier versión(xml/json) y me sale este error; la otra prueba que hice es primero porbar los POST(funciona sin problemas) después ejecuto GET(funcioana sin problemas) pero quiero volver a probar con POST y me sale el mismo error, a que se débe? de hecho ni entra al metodo, es como si no lo encontrara después de la ejecución con los GET??? espero me puedas(an) ayudar, gracias. Saludos.
Muchas gracias por el aporte, me ha ayudado a entender lo que es Restful, pero me surge la duda, ¿es posible manejar archivos, es decir para subir y bajar archivos al servidor, de una forma sencilla a partir de Restful?. Gracias.
ResponderBorrarcon netbeans 7.3 al probar los rest, no me sale el arbol de la iquierda en el navegador
ResponderBorrarQue tal
ResponderBorrarRecientemente estoy aprendiendo de RESTfull
eh creado un ws en una aplicacion
pero ahora quiero consumirlo desde otra aplicacion cliente
para ello, le doy crear RESTfull java cliente, y obtengo el ws desde el proyecto que lo contiene
Ahora bien, al checar ello, me da como resultado los metodos del ws pero de tipo generico
como puedo obtener el objeto en si de ello y poder usarlo para presentarlo en un xhtml?
Disculpa si no me exprese bien y me enredè