Jakarta EE 11 - Jakarta Data - Parte 1
A la fecha de esta publicación, aún no se ha lanzado oficialmente Jakarta EE 11. Pero ya hay unos avances de ciertas especificaciones, como Jakarta Data la que yo considero una de las más interesantes e importantes.
En este post veremos cómo podemos configurar nuestro proyecto con Jakarta EE 11 + Jakarta Data, utilizando la implementación de Hibernate sobre Payara Server.
Creación del proyecto
Para comenzar, crearemos un proyecto utilizando un arquetipo de Maven:
mvn archetype:generate \
-DarchetypeGroupId=com.apuntesdejava \
-DarchetypeArtifactId=jakarta-ee-essentials \
-DarchetypeVersion=0.0.2 \
-DjakartaProfile=core
Con este comando mostrará la siguiente pantalla para completar los parámetros
como groupId
, artifactId
, version
y
package
:
Que finalmente creará el proyecto con la siguiente estructura:
Ese arquetipo lo he creado, y el detalle de sus parámetros lo pueden encontrar aquí: https://jakarta-coffee-builder.github.io/pages/archetype.html
Agregando dependencias
Comenzaremos por agregar las dependencias necesarias. Necesitaremos:
- El driver de la base de datos. Para este ejemplo usaremos h2.
- Hibernate ORM
- La declaración de
jakarta.data-api
<properties>
<!-- otras propiedades -->
<hibernate.version>6.6.9.Final</hibernate.version>
</properties>
<dependencies>
<!-- ... otras dependencias -->
<dependency>
<groupId>jakarta.data</groupId>
<artifactId>jakarta.data-api</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.3.232</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
Y debemos configurar el plugin para generar el código para Hibernate
<build>
<plugins>
<!-- otros plugins -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>${hibernate.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Configuración del DataSource
Existen dos maneras estándar para declarar un datasource: en el archivo
web.xml
y declarándolo en un clase. Esta vez lo haremos en el
archivo web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<data-source>
<name>java:global/ExampleDataSource</name>
<class-name>org.h2.jdbcx.JdbcDataSource</class-name>
<url>jdbc:h2:mem:</url>
<user>sa</user>
<password>sa</password>
<property>
<name>fish.payara.log-jdbc-calls</name>
<value>true</value>
</property>
</data-source>
</web-app>
Configuración de persistence.xml
Ahora, como toda configuración de persistencia, necesitamos establecer la Unidad de Persisencia y asociarla al DataSource
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="defaultPU">
<!-- utilizar el provider de hibernate -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:global/ExampleDataSource</jta-data-source>
<properties>
<!-- estos son necesarios -->
<property name="hibernate.enhancer.enableDirtyTracking" value="false"/>
<property name="hibernate.enhancer.enableLazyInitialization" value="false"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
<!-- dicen que no es necesario este campo, pero si no se pone, no funciona -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- otros atributos -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
El endpoint
Necesitamos manejar el endpoint, así que tendremos las siguientes record para ser usados como request y response
CoffeeRequest.java
package com.example.example.jakarta.data.dto;
public record CoffeeRequest(String name, Double price) {
}
CoffeeResponse
package com.example.example.jakarta.data.dto;
import com.example.example.jakarta.data.entity.CoffeeEntity;
public record CoffeeResponse(Long id, String name, Double price) {
public static CoffeeResponse of(CoffeeEntity coffeeEntity) {
return new CoffeeResponse(coffeeEntity.getId(),
coffeeEntity.getName(),
coffeeEntity.getPrice());
}
}
También necesitaremos el endpoint en sí:
CoffeeResource.java
package com.example.example.jakarta.data.resources;
import com.example.example.jakarta.data.dto.CoffeeRequest;
import com.example.example.jakarta.data.service.CoffeeService;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import jakarta.ws.rs.core.Response;
@Path("coffee")
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
public class CoffeeResource {
@Inject
private CoffeeService coffeeService;
@POST
public Response save(CoffeeRequest coffeeRequest) {
var coffeeSaved = coffeeService.create(coffeeRequest);
return Response.ok(coffeeSaved).build();
}
@GET
public Response list() {
var coffeeList = coffeeService.listAll();
return Response.ok(coffeeList).build();
}
}
La clase CoffeeService
se encargará de hacer convertir las
entidades a las clases request y response, ya que no deberíamos exponer la
entidad en sí.
Por tanto, ésta es la clase:
CoffeeService.java
package com.example.example.jakarta.data.service;
import com.example.example.jakarta.data.dto.CoffeeRequest;
import com.example.example.jakarta.data.dto.CoffeeResponse;
import com.example.example.jakarta.data.entity.CoffeeEntity;
import com.example.example.jakarta.data.repository.CoffeeRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
@ApplicationScoped
public class CoffeeService {
@Inject
private CoffeeRepository coffeeRepository;
public List<CoffeeResponse> listAll() {
return coffeeRepository.findAll()
.map(CoffeeResponse::of)
.toList();
}
public CoffeeResponse create(CoffeeRequest coffeeRequest) {
var coffeeEntity = new CoffeeEntity();
coffeeEntity.setName(coffeeRequest.name());
coffeeEntity.setPrice(coffeeRequest.price());
var saved = coffeeRepository.save(coffeeEntity);
return CoffeeResponse.of(saved);
}
}
La capa de persistencia
Ahora bien, esta es la capa de persistencia en sí. Solo heredar la interfaz
jakarta.data.repository.CrudRepository
y tendríamos todas las
funcionalidades ya hechas
Entidad CoffeeEntity.java
package com.example.example.jakarta.data.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import static jakarta.persistence.GenerationType.IDENTITY;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "coffee")
public class CoffeeEntity {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
@Column (
name = "coffee_name",
length = 100,
unique = true,
nullable = false
)
private String name;
private Double price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
Repositorio CoffeeRepository.java
package com.example.example.jakarta.data.repository;
import com.example.example.jakarta.data.entity.CoffeeEntity;
import jakarta.data.repository.CrudRepository;
import jakarta.data.repository.Repository;
@Repository
public interface CoffeeRepository extends CrudRepository<CoffeeEntity, Long>{
}
Código fuente
El código fuente está disponible aquí: https://github.com/apuntesdejava/example-jakarta-data y también incluye ejemplos de cómo invocar a los endpoint.
Vídeo
También hay una explicación en vivo de este código
Comentarios
Publicar un comentario
Si quieres hacer una pregunta más específica, hazla en los foros que tenemos habilitados en Google Groups
Ah! solo se permiten comentarios de usuarios registrados. Si tienes OpenID, bienvenido! Puedes obtener su OpenID, aquí: http://openid.net/