PUT /books/{id}

Es el encargado de actualizar objetos Book. Dado que Review es una clase hija de Book a través de una relación composite, este servicio también permite la actualización de reviews asociados con un book.

Parámetros

Nombre Ubicación Descripción Requerido Esquema
id path ID del objeto book a actualizar Integer
body body Objeto book nuevo Representación full

Respuesta

Código Descripción Cuerpo
200 Objeto book actualizado Representación full
409 La editorial asociada no existe Mensaje de error
500 No se pudo crear el objeto book Mensaje de error

JAX-RS recibe la petición en un String con formato JSON

Una vez el front envía la petición, el servlet de JAX-RS la recibe. Basándose en la URL solicitada (/books), y en el método HTTP (PUT), JAX-RS busca una clase con la anotación @Path('books') y que tenga un método con la anotación @PUT y @Path("{id: \\d+}"), el cual será ejecutado para responder a la solicitud.

@Path("books")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class BookResource {

    // ...

    @PUT
    @Path("{id: \\d+}")
    public BookDTO updateBook(@PathParam("id") Long id, BookDTO dto) {
        // implementación
    }

    //...
}

Sin embargo, se observa que la firma del método indica que recibe como parámetro una instancia de BookDTO, pero la petición trae un string. Es aquí cuando JAX-B hace su trabajo.

Dado que la clase BookDTO está anotada con @XmlRootElement, JAX-RS puede deserializar el JSON y mapear sus datos con los de BookDTO usando JAX-B. Para esto, JAX-B busca en BookDTO los accesores (getters y setters) cuyo nombre corresponda a los atributos en el JSON. Por ejemplo, para la propiedad name buscaría un método setName, para la propiedad isbn buscaría el método setIsbn, etc. Así, JAX-B deserializa el JSON, crea la instancia de BookDTO y la entrega al método para su ejecución.

Conversión del DTO a Entity

Ya tenemos en el recurso un DTO con los datos. Sin embargo, el API de la lógica recibe objetos Entity, para lo cual es necesario realizar la conversión entre estas dos estructuras. Para esto, el converter ofrece un conjunto de métodos, con nombres compuestos de dos partes que indican el objetivo de cada uno:

La primer parte del nombre, y su significad, puede ser alguno de los siguientes

  • ref: Métodos que se usan cuando se transmitirá información como una referencia desde otro, los cuales usualmente son id y name. Por ejemplo, cuando se carga una instancia de Book y se quiere saber el nombre de la Editorial a la que está asociada, se usa este método para convertir la editorial. De esta manera, se evita la transmisión de datos innecesarios.
  • basic: Métodos que se usan dentro de los métodos list. Estos realizan la conversión de todos los atributos de la entidad a excepción de las colecciones. Esto con el fin de evitar consultas exhaustivas que contengan toda la base de datos. Además que al listar registros de una entidad no se visualiza sus colecciones.
  • full: Métodos que se usan cuando se consulta una instancia específica de una entidad. Estos convierten todos los atributos de una entidad incluyendo las colecciones composite. Se usas cuando se desea editar una instancia.
  • list: Métodos usados para convertir listas de registros. Estos por dentro usan los métodos basic.
  • child: Métodos para convertir instancias de DTO a Entity que son hijas de una relación composite, para la cual asignan la instancia del padre que le corresponde.

La segunda parte del nombre indica el sentido de la conversión:

  • Entity2DTO: Realiza la conversión de instancias de Entity a DTO
  • DTO2Entity: Realiza la conversión de instancias de DTO a Entity

En el caso del PUT se usa el método fullDTO2Entity (el cual por dentro invoca a basicDTO2Entity), que permite realizar la conversión de todos los atributos de book:

private static BookEntity basicDTO2Entity(BookDTO dto) {
    if (dto != null) {
        BookEntity entity = new BookEntity();
        entity.setId(dto.getId());
        entity.setName(dto.getName());
        entity.setDescription(dto.getDescription());
        entity.setIsbn(dto.getIsbn());
        entity.setImage(dto.getImage());
        entity.setPublishDate(dto.getPublishDate());
        entity.setEditorial(EditorialConverter.refDTO2Entity(dto.getEditorial()));

        return entity;
    } else {
        return null;
    }
}

public static BookEntity fullDTO2Entity(BookDTO dto) {
    if (dto != null) {
        BookEntity entity = basicDTO2Entity(dto);
        entity.setReviews(ReviewConverter.childListDTO2Entity(dto.getReviews(), entity));
        return entity;
    } else {
        return null;
    }
}

Una vez el converter haga su trabajo, se tendrá disponible la instancia de Entity para invocar al servicio del API de la lógica:

Invocación de API de lógica

Para la invocación de la lógica, se inyectó una instancia de IBookLogic, cuya inicialización es resuelta por el contenedor:

@Inject
private IBookLogic bookLogic; // Inyección de instancia de IBookLogic

@PUT
@Path("{id: \\d+}")
public BookDTO updateBook(@PathParam("id") Long id, BookDTO dto) {
    logger.log(Level.INFO, "Se ejecuta método updateBook con id={0}", id);
    BookEntity entity = BookConverter.fullDTO2Entity(dto);
    entity.setId(id);
    BookEntity savedBook = bookLogic.updateBook(entity);
    return BookConverter.fullEntity2DTO(savedBook);
}

La implementación de este método consulta la entidad actual, asigna a la nueva los mismos autores de la ya existente (ya que estos no deben modificarse), y persiste la entidad nueva usando el bean de persistencia.

@Inject
private BookPersistence persistence;

@Override
public BookEntity updateBook(BookEntity entity) {
    logger.log(Level.INFO, "Inicia proceso de actualizar libro con id={0}", entity.getId());
    BookEntity oldEntity = persistence.find(entity.getId());
    entity.setAuthors(oldEntity.getAuthors());
    BookEntity newEntity = persistence.update(entity);
    logger.log(Level.INFO, "Termina proceso de actualizar libro con id={0}", entity.getId());
    return newEntity;
}

Invocación de capa de persistencia

El método update de BookPersistence recibe la entidad de book, a través de un EntityManager lo actualiza en base de datos con el método merge y finalmente retorna la entidad modificada.

public BookEntity update(BookEntity entity) {
    logger.log(Level.INFO, "Actualizando libro con id={0}", entity.getId());
    return em.merge(entity);
}

Retorno de la respuesta

Una vez la persistencia retorna la entidad modificada, la capa lógica la retorna esta misma entidad hasta el servicio updateBook sin modificarla.

Una vez el servicio recibe la respuesta, necesita serializarla en formato JSON para poder transmitirla con el protocolo HTTP. Aquí es donde JAX-B vuelve a entrar en acción. Al igual que JAX-B puede deserializar los DTO, también tiene la capacidad de serializarlo, mapeando los atributos del objeto DTO a propiedades del objeto JSON. Así, cada método accesor (getter) es mapeado a una propiedad de la instancia. En este caso, book quedaría como un objeto JSON, donde cada propiedad accesible con un get, está disponible como atributo del JSON.

En el front-end, $http recibe la respuesta y la pone a disposición del promise como argumento. De esta manera, quien invoque al servicio (en este caso el controlador) puede usar la respuesta para lo que desee. Sin embargo, en este caso el controlador ignora la respuesta dado que no la necesita.

this.saveRecord = function () {
    return svc.saveRecord($scope.currentRecord).then(function () {
        self.fetchRecords();
    }, responseError);
};

results matching ""

    No results matching ""