Como configurar jOOQ en un proyecto maven

En este post voy a explicar como montar un sencillo PoC con la librería jOOQ, para el acceso a base de datos usando queries SQL «implementadas» directamente con clases y métodos Java. La idea es detallar los pasos para poder usarlo, en un proyecto maven. Más adelante es probable que escriba otro post explicando como integrarlo con Spring.

Concepto:
jOOQ (Java Object Oriented Querying) es una librería Java para el acceso a base de datos cuya principal característica es que define un DSL en java, con fluent interface, para la generación de queries, tal cual las generaríamos en SQL.

Podemos verlo con un ejemplo. Esto sería una query en SQL:

SELECT BOOK_CHARACTER.NAME
FROM BOOK_CHARACTER
WHERE BOOK_CHARACTER.ID = 1

Y esto sería con jOOQ, en java:

DSL.using(connection)
    .select(Tables.BOOK_CHARACTER.NAME)
    .from(Tables.BOOK_CHARACTER)
    .where(Tables.BOOK_CHARACTER.ID.equal(1))
    .fetch();

Su principal ventaja es la seguridad que ofrece: mientras que al picarnos una query SQL y lanzarla desde Java con JDBC o similar, hasta que no la ejecutamos, no podemos estar seguro de que no hemos metido la pata en la query. Con jOOQ, cualquier error sintáctico o de tipos es «controlado» por el compilador. Es decir, si la query no es correcta… ¡no compila!

El PoC que voy a montar va a ser una aplicación maven simple en la que usaremos H2 como base de datos y usaré logback para logs. En el voy a mostrar principalmente como se configura jOOQ.

Entorno usado:
Java JDK 1.8
Maven 3.2.1
Git 1.9.5
IDE Intellij 14.0.2 Ultimate version

Pasos:

1. Crearemos un nuevo proyecto Maven igual que lo creamos en mi primer post de Spring Boot desde Intellij (pasos 1 al 4). Podemos hacerlo con el archetype maven ‘quickstart’. Yo para este PoC he usado un archetype creado por mi que es un quickstart actualizado. Os lo podeís descargar de mi github: https://github.com/edwise/maven-archetypes

2. Añadimos a nuestro pom.xml las dependencias necesarias, tanto de jOOQ como de H2, logback y slfj4 ( estos dos últimos para logs). Serían todas estas:

...
<dependencies>
    <dependency>
        <groupId>org.jooq</groupId>
        <artifactId>jooq</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.jooq</groupId>
        <artifactId>jooq-meta</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.jooq</groupId>
        <artifactId>jooq-codegen</artifactId>
        <version>3.5.1</version>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.182</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.10</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.1.2</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.1.2</version>
    </dependency>
    ...
</dependencies> 
...

3. Dado que vamos a usar H2 como base de datos, y en memoria, vamos a picarnos un pequeño script para recrear la base de datos cada vez que conectamos a ella. Creamos una subcarpeta ‘resources’ y en ella creamos el siguiente fichero:

create schema if not exists pocjooq;

use schema pocjooq;

create table if not exists book_character (id integer primary key, name varchar(30));

delete from book_character;
insert into book_character values (1, 'Saruman');
insert into book_character values (2, 'Gandalf');
insert into book_character values (3, 'Aragorn');
insert into book_character values (4, 'Samwise');
insert into book_character values (5, 'Frodo');

commit;

4. Ahora vamos a hacer que jOOQ autogenere sus clases. jOOQ realmente lo que hace es leer toda la estructura de nuestra base de datos y generar código y clases basado en nuestras tablas y columnas. Se puede hacer de varias maneras (más info en su documentación), pero para nuestro caso lo vamos a hacer con el plugin maven de jOOQ. Añadimos lo siguiente a nuestro pom.xml:

...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <testSource>1.8</testSource>
                <testTarget>1.8</testTarget>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.jooq</groupId>
            <artifactId>jooq-codegen-maven</artifactId>
            <version>3.5.1</version>

            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>

            <dependencies>
                <dependency>
                    <groupId>com.h2database</groupId>
                    <artifactId>h2</artifactId>
                    <version>1.4.182</version>
                </dependency>
            </dependencies>

            <configuration>
                <jdbc>
                    <driver>org.h2.Driver</driver>
                    <url>jdbc:h2:mem:pocjooq;INIT=RUNSCRIPT FROM 'src/main/resources/populate_db.sql'</url>
                    <user>sa</user>
                    <password></password>
                </jdbc>

                <generator>
                    <name>org.jooq.util.JavaGenerator</name>
                    <database>
                        <name>org.jooq.util.h2.H2Database</name>
                        <includes>.*</includes>
                        <excludes></excludes>
                        <inputSchema>POCJOOQ</inputSchema>
                    </database>
                    <target>
                        <packageName>org.jooq.util.maven.generated</packageName>
                        <directory>target/generated-sources/jooq</directory>
                    </target>
                </generator>
            </configuration>
        </plugin>
    </plugins>
</build>
...

He añadido primero el maven-compiler-plugin, necesario para poder decirle a maven que compile con la JDK 8.
El plugin de jOOQ para la generación de código, jooq-codegen-maven, consta de lo siguiente:
– El ‘goal’ en el que se generará el código (‘generate’ en este caso).
– Dependencias que necesita: en nuestro caso la dependencia de H2 para la base de datos.
– Configuración de acceso a la base de datos (url, user, pass y driver). En nuestro caso, los por defecto de H2 para una base de datos en memoria. En la url de la conexión a H2, le pasamos como script para ejecutar al arrancar, el fichero .sql creado anteriormente.
– Configuración del generador de código de jOOQ (tag ‘generator’): el generador usado (JavaGenerator), información sobre la base de datos: tipo de base de datos, si queremos excluir algo y el schema principal (‘POCJOOQ’ en nuestro caso), así como directorio destino donde se generará el código, y nombre de paquetería.

5. Ahora podemos, o bien ejecutar ese plugin (desde consola con ‘mvn jooq-codegen:generate’ o desde la tool window de maven en intellij), o bien compilar el proyecto completo (con maven también), y generará todas las clases. Podemos acceder a la ruta ‘target/generated-sources/jooq’ para ver el código generado. La que más usaremos será la clase ‘Tables.java’.

6. jOOQ necesita una conexión jdbc para funcionar. Para ello he creado una clase ‘DBConnector.java’, podéis verla directamente en github aquí. No es necesario que entremos mucho en detalle, lo importante es que realiza una conexión con la misma base de datos usada en el plugin, y nos devuelve un clásico ‘java.sql.Connection’. Los métodos para obtener la conexión y para cerrarla son los siguientes:

// Obtener una conexión a la BD
java.sql.Connection connection = DBConnector.getInstance().connection();

// Cerrar la conexión a la BD
DBConnector.getInstance().closeConnection();

7. Con esto ya podemos desarrollar directamente queries en java con jOOQ. Vamos a realizar unos ejemplos, para ello crearemos una clase de test jUnit. Empezaremos implementando los métodos @Before y @After, para obtener la conexión y para cerrarla:

package com.edwise.pocs.jooq;

// imports...

public class JOOQExampleTest {
    private final static Logger log = LoggerFactory.getLogger(JOOQExampleTest.class);

    private DSLContext jooqDSL;

    @Before
    public void setUp() {
        jooqDSL = DSL.using(DBConnector.getInstance().connection(), SQLDialect.H2);
    }

    @After
    public void tearDown() {
        DBConnector.getInstance().closeConnection();
    }
}

Aparte de obtener un logger, declaramos un atributo de tipo DSLContext y lo inicializamos en el método @Before, pasandole la conexión, y el el tipo de base de datos (H2 en este caso). Con ese objeto DSLContext podemos llamar a todos los métodos de jOOQ.
En el método after cerramos la conexión.

8. Implementamos un pequeño ejemplo de una select que nos devuelva todo el contenido de la única tabla de la base de datos, BOOK_CHARACTER:

    @Test
    public void testSelectAll() {
        Result<Record> result = jooqDSL
                .select()
                .from(Tables.BOOK_CHARACTER)
                .fetch();

        log.info("Resultado query: \n{}", result.toString());
    }

Usamos para ello el DSLContext definido, y sobre el, con fluent interface, montamos la query. Para los nombres de tablas o columnas usamos la clase Tables (si importamos estáticamente toda el contenido de esa clase sería incluso más legible el ejemplo).
El resultado nos lo devuelve en un objeto Result. Al ejecutar ese método veremos que jOOQ tiene todos sus ‘toString()’ bastante currados, el log de este método nos mostrará algo como esto:

18:34:06.953 [main] INFO  com.edwise.pocs.jooq.JOOQExampleTest - Resultado query:
+----+-------+
|  ID|NAME   |
+----+-------+
|   1|Saruman|
|   2|Gandalf|
|   3|Aragorn|
|   4|Samwise|
|   5|Frodo  |
+----+-------+

Más aún, como tenemos logback configurado con su configuración por defecto, está activado el nivel DEBUG. jOOQ muestra mucha información a nivel DEBUG. El resultado por consola completo de ejecuta el test anterior muestra, antes de ese log, logs de jOOQ en el que muestra la query resultante al «traducirla» a SQL, el tiempo que le ha llevado, el resultado obtenido, etc.

9. Como último ejemplo, vamos a implementar un test que haga uso de los Streams de Java 8. jOOQ es totalmente compatible con Java 8:

    @Test
    public void testStreamForEachJava8() {
        jooqDSL.selectFrom(Tables.BOOK_CHARACTER)
                .where(Tables.BOOK_CHARACTER.ID.in(2, 3))
                .orderBy(Tables.BOOK_CHARACTER.NAME)
                .fetch()
                .stream()
                .map(bookCharacter -> bookCharacter.getName().toUpperCase())
                .forEach(s -> log.info("Name: {}", s));
    }

En este caso obtenemos solo los registros con ids 2 y 3, los ordenamos por nombre, y mediante el uso de streams lo ponemos en mayúsculas y los logamos.
El resultado por consola es el siguiente:

18:34:23.038 [main] INFO  com.edwise.pocs.jooq.JOOQExampleTest - Name: ARAGORN
18:34:23.038 [main] INFO  com.edwise.pocs.jooq.JOOQExampleTest - Name: GANDALF

No me enrollo más, que el post era solo para configurarlo 😉 . jOOQ por supuesto proporciona métodos para inserts, updates, deletes… El manual es bastante didáctico y completo. Además, en su web, en la sección ‘Community’ tenéis muchos tutoriales, posts, etc. Echadles un vistazo.

Por último, como siempre, podéis descargar el proyecto completo en mi github, proyecto ‘jooq-example’ 🙂

Spring Boot series: autoconfiguración de Jackson

Para comenzar este año 2015, vuelvo con los minipost sobre Spring Boot. Esta vez explicaré, con un ejemplo, como configurar el parseo de objetos a json y viceversa con Jackson, muy fácilmente en Spring Boot, sobre todo desde la versión 1.2 de este.

Concepto:
Jackson es una librería Java muy potente para el procesamiento / parseo de objetos a json y viceversa. Ofrece muchas opciones y posibilidades, anotaciones propias, etc. Suele ser casi la librería estándar para esto en los proyectos en los que he trabajado.

En un típico proyecto Spring, como los que he explicado en otros posts, como el de implementar un servicio REST con Spring, suele venir por defecto Jackson.
También en el post de implementar un servicio REST con JAX-RS usé Jackson para lo propio.

Para el caso de Spring, suele ser suficiente con cargar en el contexto un Bean de Jackson (suele ser un ObjectMapper), configurado tal cual lo necesitemos, y ya automáticamente nuestros REST usarán ese «mapper».

Para este ejemplo voy a explicar como configurar Jackson en Spring Boot 1.2 (es más facil aún en esta nueva versión), para el uso de fechas con la librería Joda-time, así como también para fechas con la nueva API de Java 8 (la famosa especificación JSR-310).

Entorno usado:
Java JDK 1.8
Maven 3.2.1
Git 1.9.5
IDE Intellij 14.0.2 Ultimate version
Lombok

Pasos:

Vamos a implementar una pequeña aplicación Spring Boot, con un entity Info y un controller que será un mini servicio REST: vamos a implementar solo el GET de un elemento y el POST, sin servicio ni repositorios. Esto no lo haríamos así si quisiéramos tener una implementación por capas correcta, claro :P. Pero para nuestro ejemplo, es suficiente.
Esta entity Info tendrá un atributo de tipo fecha y hora (LocalDateTime de joda-time primero, y LocalDateTime de la API Java 8 Date después). Y para simplificar el código, usaremos Lombok
El proyecto completo os lo podéis descargar como siempre de mi github, proyecto springboot-series-jackson. Está montado con la librería joda-time, pero también está comentado para usar la librería JSR-310.

¡Al lio!

Con Joda-time

1. Añadimos a nuestro pom.xml de maven lo necesario para montar una aplicación Spring Boot básica:

    ...
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        ...
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>               
    ...

Lo de siempre: la dependencia «padre» del proyecto Spring Boot, la dependencia para el starter web y el plugin para maven (para poder arrancarlo). Nada nuevo.

2. Añadimos la dependencia de la librería joda, así como de la sublibrería jackson para joda:

    ...
    <dependencies>
        ...
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
            <version>2.4.4</version>
        </dependency>
        ...
    </dependencies>
    ...

La librería core de jackson ya nos la incluye Spring, solo necesitamos añadirle la libreria «third party» para el tipo joda-time.
En nuestro pom.xml añadimos también lombok, junit y alguna otra librería. El pom.xml completo lo tenéis aquí.

3. Creamos nuestra clase Application para el arranque de Spring Boot:

package com.edwise.springbootseries.jackson;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

La anotación @SpringBootApplication es nueva en la versión 1.2. Simplemente es un ‘atajo’ para las tres anotaciones @ComponentScan, @EnableAutoConfiguration y @Configuration. Nada más.

4. Creamos nuestra clase entity, Info:

package com.edwise.springbootseries.jackson.entity;

import lombok.Data;
import lombok.experimental.Accessors;
import org.joda.time.LocalDateTime;

@Data
@Accessors(chain = true)
public class Info {

    private long id;
    private String info;
    private LocalDateTime creationDateTime;
}

Consta solo de tres atributos, un long, un String y un LocalDateTime. Usamos lombok para que genere el código típico (getters, setter, toString…).

5. Creamos nuestro RestController, solo con un GET y un POST, mockeados (ni servicio, ni repositorio, etc):

package com.edwise.springbootseries.jackson.controller;

import com.edwise.springbootseries.jackson.entity.Info;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.joda.time.LocalDateTime;

@RestController
@RequestMapping("/api/info/")
public class InfoController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, value = "{id}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Info getInfo(@PathVariable long id) {
        return new Info()
                .setId(id)
                .setInfo("Info 1234")
                .setCreationDateTime(new LocalDateTime(2001, 12, 12, 13, 40, 30));
    }

    @RequestMapping(method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity createInfo(@RequestBody Info info) {
        return new ResponseEntity<>(info.setId(1234), HttpStatus.CREATED);
    }
}

Nada nuevo tampoco: un método para obtener con GET un Info, por id, y un método para crear un Info completo, por POST.

6. Si probamos a arrancar nuestra aplicación Spring Boot, y accedemos (mediante navegador o cliente REST mejor) a http://localhost:8080/api/info/1, nos devolverá algo como esto:

{
  "id": 1,
  "info": "Info 1234",
  "creationDateTime": [
    2001,
    12,
    12,
    13,
    40,
    30,
    0
  ]
}

Si, un json. Spring Boot nos carga automáticamente en el contexto un bean de jackson para realizar ese parseo. Además, con la nueva versión 1.2, solo por tener en el proyecto la librería jackson de joda (jackson-datatype-joda), también nos carga el JodaModule que usábamos en los posts de montar un servicio REST el solito. No tenemos que hacer nada más.

7. El formato en el que se muestra el atributo fecha por defecto es una especie de array con los valores. Si queremos que sea algo como esto: «2001-12-12T13:40:30.000», en principio tendríamos que añadir al mapper la configuración siguiente, como ya hicimos en alguno de los posts anteriores:

   ...
   objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
   ...

No es necesario hacerlo así. Esto también ha sido simplificado en Spring Boot, y lo único que tenemos que hacer es añadir, en un archivo .properties (o .yml) la configuración que queramos. En este caso, crearíamos un application.properties bajo una carpeta ‘resources’:

spring.jackson.serialization.write_dates_as_timestamps=false

Si probamos a arrancar la aplicación, y accedemos otra vez http://localhost:8080/api/info/1, ahora nos devolverá algo así:

{
  "id": 1,
  "info": "Info 1234",
  "creationDateTime": "2001-12-12T13:40:30.000"
}

Resumiendo: hemos configurado Jackson, con joda-time, y con un formato concreto de fecha, simplemente añadiendo las librerías al proyecto y añadiendo una propiedad a un fichero properties. El resto, se ha encargado Spring Boot 🙂

Con la API Date Java 8 (JSR-310)

Los pasos son casí los mismos que en el anterior, solo voy a detallar los que cambian:

2. Añadimos al pom la dependencia de la librería jackson la nueva api Date en Java 8:

  ...
    <dependencies>
        ...
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.4.4</version>
        </dependency>
        ...
    </dependencies>
  ...

4. Creamos nuestra entity Info, pero ahora con el LocalDateTime de Java 8… no cambia nada más que el import:

...
import java.time.LocalDateTime;
...

5. Creamos nuestro RestController: solo cambia el import del LocalDateTime, y la forma en que lo creamos en el setter:

...
import java.time.LocalDateTime;

@RestController
@RequestMapping("/api/info/")
public class InfoController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(method = RequestMethod.GET, value = "{id}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Info getInfo(@PathVariable long id) {
        return new Info()
                .setId(id)
                .setInfo("Info 1234")
                .setCreationDateTime(LocalDateTime.of(2001, 12, 12, 13, 40, 30));
    }

    ...
}

¡Y listo! el resto de pasos son exactamente iguales: añadiríamos la misma propiedad a nuestro application.properties, y ya tendríamos Jackson funcionando en nuestra aplicación Spring Boot, esta vez usando la nueva API Date de Java 8. Y, de la misma manera, lo único que hemos necesitado para configurarla es añadir la librería jackson de la JSR-310 al proyecto, añadir esa propiedad a nuestro properties.

Como comente antes, el proyecto completo está en mi github, proyecto springboot-series-jackson 🙂