viernes, 11 de septiembre de 2020

Apache Camel: CRUD con JPA

 Ya hemos visto anteriormente otros posts de Apache Camel. Hoy veremos como hacer las operaciones de BBDD con JPA.

Como siempre empezaremos por la configuración. En cuanto a librerías añadimos las siguientes:

  • El componente camel-jpa-starter de Apache Camel que nos permitirá usar el componente JPA. 
  • La librería hibernate-entitymanager que nos permitirá utilizar las anotaciones de hibernate.
Debemos recordar un poco cual es el fundamento de JPA y es la cualidad de poder manejar datos relacionales como objetos Java. Permitiéndonos manejar más fácilmente dentro de clases Java la información de una BBDD. Estas clases serán las denominadas Entidades. Y como parte de la API de JPA se encuentra JPQL, un lenguaje que nos permitirá realizar consultas sobre esas entidades. 

Gracias a Spring boot y los starter no tendremos que realizar mucha configuración, pero a nivel de la clase Java deberemos añadir una cuantas anotaciones:

@Entity(name = "BOOK")
@Table(name = "BOOK")
@NamedQuery(name = "findAll", query = "SELECT b FROM BOOK b")
public class Book {

    @JsonProperty
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @JsonProperty
    private String name;
    @JsonProperty
    private String author;
}

@Entity nos permitirá indicar que es una clase a ser gestionada a través de JPA. Con @Id indicaremos que cual es su primary key y con @GeneratedValue se gestionará su generación, en el caso de MySQL será Auto. 

En el primer ejemplo que veremos será muy similar al del componente SQL solo que usaremos JPQL y debemos indicar cual es la entidad sobre la que vamos a realizar la consulta. 

rest().get("book/{id}").description("Details of an book by id")
   .outType(Book.class).produces(MediaType.APPLICATION_JSON_VALUE).route()
   .toD("jpa://" + Book.class.getName() + "?query=select b from " + Book.class.getName()+ " b where b.id= ${header.id}", 5)
   .log("--- select a book ${body} ---");

Lo primero que tenemos que hacer es indicar cual es la entidad sobre la cual vamos a trabajar. Luego si queremos hacer una consulta JPQL debemos añadir el parámetro 'query'. 

Además en este caso concreto queremos obtener un libro en base a un identificador que viene como parámetro de la request, a través del lenguaje de expresión. Para poder conseguir esto utilizaremos el método ToD en vez de To. El cual nos permitirá enrutar de forma dinámica el flujo y que sea interpretado el lenguaje de expresión.

Otra de las cualidades de JPA son las namedQuery, las cuales nos permiten definir consultas JPQL a nivel de la clase. En el siguiente ejemplo veremos como utilizar las namedQueries a través del componente JPA. La query a utilizar es findAll.

rest().get("book").produces(MediaType.APPLICATION_JSON_VALUE).route()
      .to("jpa://"+ Book.class.getName() + "?resultClass="+ Book.class.getName() + "&namedQuery=findAll")
      .log("---select all books---");

En este segundo ejemplo podemos ver la diferencia de usar el método outType para indicar el tipo de objeto a la salida. O si por lo contrario podemos indicarlo a través del query parameter resultClass del propio componente JPA

También podemos hacer operaciones que nos permitan guardar o persistir dichos objetos en la BBDD. Como podemos ver en el ejemplo, en el caso de almacenar el valor, el funcionamiento es muy sencillo. Solo debemos indicar el objeto a persistir y utilizar el query parameter usePersist para indicar que debe almacenar el objeto. Por supuesto, en el flujo deben ir los datos del objeto a guardar. 

rest().post("book").produces(MediaType.APPLICATION_JSON_VALUE).type(Book.class)
      .route().routeId("postBookRoute").log("--- binded ${body} ---")
      .to("jpa:" + Book.class.getName() + "?usePersist=true")
      .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(201)).setBody(constant(null));

Para el caso de las actualizaciones y borrado de datos, necesitamos utilizar el query parameter useExecuteUpdate. En este ejemplo haremos uso del método process para crear el objeto a actualizar a partir de los datos de la cabecera y el cuerpo del mensaje. 

rest().put("book/{id}").produces(MediaType.APPLICATION_JSON_VALUE).type(Book.class).route().choice()
      .when().simple("${header.id} < 1").bean(BookRouter.class, "negativeId").otherwise()
      .process(new Processor() {
          @Override
          public void process(final Exchange exchange) {
              Book book = exchange.getIn().getBody(Book.class);
              Integer id = Integer.valueOf(exchange.getIn().getHeader("id").toString());
              book.setId(id);
              exchange.getIn().setBody(book, Book.class);
          }
      }).to("jpa:" + Book.class.getName() + "?useExecuteUpdate=true")
      .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200)).setBody(constant(null));

Por último veremos el ejemplo asociado al borrado de datos. Y de paso veremos como realizar consultas nativas. Es decir utilizaremos lenguaje SQL común y no JPQL. 

rest().delete("book/{id}").produces(MediaType.APPLICATION_JSON_VALUE).route()
      .toD("jpa:com.example.home.ApacheCamelRestExample.pojo.Book"
            + "?nativeQuery=DELETE FROM library.BOOK where id = ${header.id}&useExecuteUpdate=true")
      .setHeader(Exchange.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON_VALUE))
      .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200)).setBody(constant(null));

Como veis algunas consultas pueden ser algo más difíciles que usar simple SQL. Pero también otras se simplifican muchísimo y siempre estamos trabajando con objetos. Lo cual, al programar en un lenguaje orientado a objetos es muy de agradecer. 

No hay comentarios:

Publicar un comentario