POST /books

Es el encargado de crear 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 creación de reviews asociados con un book.

Parámetros

Nombre Ubicación Descripción Requerido Esquema
body body Objeto book que será creado Representación full

Respuesta

Código Descripción Cuerpo
201 Objeto book ha sido creado 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 (POST), JAX-RS busca una clase con la anotación @Path('books') y que tenga un método con la anotación @POST, el cual será ejecutado para responder a la solicitud.

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

    // ...

    @POST
    @StatusCreated
    public BookDTO createBook(BookDTO dto){
        //...
    }

    //...
}

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 POST 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

@POST
@StatusCreated
public BookDTO createBook(BookDTO dto) {
    logger.info("Se ejecuta método createBook");
    BookEntity entity = BookConverter.fullDTO2Entity(dto);
    BookEntity newEntity = bookLogic.createBook(entity); // Invocación de la lógica
    return BookConverter.fullEntity2DTO(newEntity);
}

La implementación de este método llama directamente a la persistencia sin hacer cambio alguno. Sin embargo, si se deseara implementar lógica más compleja o validaciones, este sería el lugar indicado.

@Inject
private BookPersistence persistence;

@Override
public BookEntity createBook(BookEntity entity) {
    logger.info("Inicia proceso de creación de libro");
    /* Aquí se puede incluir cualquier validación antes de llamar a la persistencia */
    persistence.create(entity);
    /* Aquí se puede incluir cualquier validación después de llamar a la persistencia */
    logger.info("Termina proceso de creación de libro");
    return entity;
}

Invocación de capa de persistencia

El método create de BookPersistence recibe la entidad de book y a través de un EntityManager lo persiste en base de datos con el método persist. Esta operación asigna automáticamente un valor a la propiedad id, la cual representa la llave primaria de la entidad, y finalmente retorna la entidad modificada.

public BookEntity create(BookEntity entity) {
    logger.info("Creando un libro nuevo");
    em.persist(entity);
    logger.info("Libro creado");
    return 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 createBook 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 ""