Hibernate con Panache en Quarkus
En esta entrada sobre Hibernate con Panache en Quarkus, vamos a ver como la librería de Hibernate con Panache nos facilitará la implementación y desarrollo de la capa de persistencia con nuestra base de datos.
Hibernate es un ORM que implementa JPA y nos ofrece un conjunto de posibilidades para trabajar con objetos relacionales. Aunque nos ofrece muchas facilidades, carece de algunas asignaciones simples y triviales, es en ese punto donde Hibernate con Panache nos ayudará en la implementación de servicios con bases de datos relacionales.
Quarkus es un framework de Java nativo para Kubernetes que funciona bajo GraalVM y HotSpot, cuyo objetivo es hacer de java un lenguaje líder en kubernetes y entornos sin servidor y ofrecer a los desarrolladores un entorno de desarrollo reactivo e imperativo.
Hands On
Vamos a crear un simple servicio en Quarkus, en el que haremos un CRUD para ver el uso de Panache en Quarkus, y como nos facilita la gestión de nuestros datos con la base de datos.
Generar proyecto Quarkus
mvn io.quarkus:quarkus-maven-plugin:1.13.7.Final:create \ -DprojectGroupId=com.refactorizando.sample \ -DprojectArtifactId=quarkus-with-hibernate-panache \ -DprojectVersion=0.0.1-SNAPSHOT \ -DclassName="com.refactorizando.samples"
Una vez hemos generado el proyecto podemos añadir las dependencias maven que necesitamos.
Dependencias Maven
A continuación las dependencias necesarias para añadir Hibernate con Panache en tu aplicación.
<dependencies> <!-- Hibernate ORM specific dependencies --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-orm-panache</artifactId> </dependency> <!-- JDBC driver dependencies --> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jdbc-postgresql</artifactId> </dependency> </dependencies>
Otra manera de añadir las dependencias es con :
mvn quarkus:add-extension -Dextensions="quarkus-hibernate-orm-panache" mvn quarkus:add-extension -Dextensions="quarkus-jdbc-postgresql"
Configuración de Panache
Una vez hemos añadido nuestras dependencias Maven en nuestra aplicación, es necesario realizar la configuración en nuestro fichero de propiedades, application.properties o application.yml.
quarkus.datasource.db-kind = postgresql quarkus.datasource.username = postgres quarkus.datasource.password = postgres quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/postgres quarkus.hibernate-orm.database.generation = drop-and-create
Vamos a ejecutar postgresql con docker para ello ejecuta el siguiente comando:
docker run --network host --name postgres -e POSTGRES_PASSWORD=postgres -d postgres
Definición de la entidad
A continuación vamos a definir una entidad que se corresponderá con su tabla en base de datos. La entidad car tendrá 4 campos:
@Entity public class Person { private Long id; private String brand; private String model; private String color; }
Con la definición de la entidad podemos realizar dos aproximaciones para poder realizar la persistencia con la base de datos, a través del patrón active record o hacer uso del patrón repository. Vamos a realizar la implementación con los dos patrones para ver las diferentes formas.
Patrón active record
Este patrón que para muchos es considerado como un anti-patrón, lo que hace es gestionar la conexión a una tabla de base de datos desde la clase que define el objeto de la tabla. Para crear este patrón lo que tendríamos que hacer es extender de PanacheEntity y esta clase nos dará una serie de métodos de gestión y acceso a base de datos por defecto.
@Entity public class Person extends PanacheEntity { private Long id; private String brand; private String model; private String color; }
Como pasa con otros frameworks como Spring Boot, si queremos que un campo no sea persistido podemos añadir @Transient.
Patrón Repository
Este es el patrón más común cuando se trabaja con bases de datos, crea una capa intermedia entre el dominio y las capas de mapeo de datos. La cual ofrece una interfaz de acceso a los objetos.
@Entity public class Car { @Id @GeneratedValue private Long id; private String brand; private String model; private String color; }
La clase PanacheEntityBase nos va a crear los getter y los setter de nuestros campos.
Una vez tenemos la entidad creada vamos a definir la capa repository.
@ApplicationScoped public class CarRepository implements PanacheRepository<Car> { public Car findByBrand(String brand){ return find("brand", brand).firstResult(); } }
Creación del recurso
Finalmente, vamos a crear nuestro recurso con 4 endpoints para nuestro CRUD. Esta clase se comunicará con la capa repository, para ello tenemos que inyectarla en nuestra clase con @Inject.
@Path("cars") @ApplicationScoped @Produces("application/json") @Consumes("application/json") @RequiredArgsConstructor public class CarResource { private static final Logger LOGGER = Logger.getLogger(CarResource.class.getName()); private final CarRepository carRepository; @GET @Transactional public List<Car> get() { return carRepository.listAll(Sort.by("brand")); } @GET @Path("{id}") public Car getSingle(@PathParam Long id) { Car entity = carRepository.findById(id); if (entity == null) { throw new WebApplicationException("Car with id of " + id + " does not exist.", 404); } return entity; } @POST @Transactional public Response create(Car car) { if (car.getId() != null) { throw new WebApplicationException("Id was invalidly set on request.", 422); } carRepository.persist(car); return Response.ok(car).status(201).build(); } @PUT @Path("{id}") @Transactional public Car update(@PathParam Long id, Car car) { if (car.getModel() == null) { throw new WebApplicationException("Car model was not set on request.", 422); } Car entity = carRepository.findById(id); if (entity == null) { throw new WebApplicationException("Car with id of " + id + " does not exist.", 404); } entity.setModel(car.getModel()); return entity; } @DELETE @Path("{id}") @Transactional public Response delete(@PathParam Long id) { Car entity = carRepository.findById(id); if (entity == null) { throw new WebApplicationException("Car with id of " + id + " does not exist.", 404); } carRepository.delete(entity); return Response.status(204).build(); } }
Una vez creado la clase resource, podremos insertar, borrar, consultar y eliminar registros de nuestra base de datos a través los endpoints creados.
Una vez finalizado todo solo nos falta ejecutar la aplicación, para ejecutar en local usaremos el siguiente comando:
mvn compile quarkus:dev
Conclusión
En esta entrada sobre Hibernate con Panache en Quarkus, hemos visto como gestionar la conexión en una base de datos con Quarkus, uno de los frameworks de Java con mayor proyección, gracias en parte a sus imágenes nativas.