Mostrando entradas con la etiqueta ORM. Mostrar todas las entradas
Mostrando entradas con la etiqueta ORM. Mostrar todas las entradas

31 de marzo de 2020

ORM por XML: Guardar subclases en SINGLE_TABLE

Ya vimos en una sesión anterior la capacidad de heredar la definición de persistencia desde las superclases con la etiqueta "mapped-superclass". En esta entrada vamos a ver cómo se guardan especializaciones distintas de un mismo tipo pudiendo recuperar todas ellas de forma única o recuperándolas como un tipo específico.

En nuestro ejemplo nuestras especializaciones son los distintos tipos de sucesos que hay en un partido (goles, tarjetas, corners, etc...) ya que todos heredan de Suceso, en nuestra API concretamente de la implementación SucesoConId.

Para persistir varias clases que heredan de un mismo tipo se puede hacer de tres formas:
  1. SINGLE_TABLE: Es la opción por defecto y la implementada por todas las librerías. Los datos de todos los subtipos se guardan en una misma tabla. Tiene el mejor rendimiento a la hora de consultar datos porque no hay que realizar ninguna unión, pero como desventaja tendremos campos null en las columnas que no usen especificaciones concretas (tienen que existir columnas para todos los campos de todas las implementaciones)
  2. JOINED: Todos los datos comunes se volcarán en una única tabla y el resto de campos se guardará en otra tabla aparte con una FK a la tabla común que lo relacione.
  3. TABLE_PER_CLASS: Esta es una opción de JPA y puede que algunas implementaciones no la contempen. Se trata de guardar cada clase entera en su propia tabla.
De estas tres formas nos vamos a centrar en SINGLE_TABLE por ser la más implementada y de mejor rendimiento aunque implique desnormalizar nuestro modelo de datos en la BD.

¿Cómo se identifica qué tipo hay en cada fila?

Como vamos a mezclar tipos distintos en la misma tabla debe haber algún mecanismo que permita saber qué tipo concreto hay en cada fila. Para esto, además de todas las columnas para los datos de todas las distintas especializaciones se va a crear una columna que sirva para discriminar el tipo. Para implementar todo lo necesario lo voy a dividir en 4 pasos:
  1. Creación de las dos especializaciones y sus DAO
  2. ORM por XML para SucesoConId y especialización de Tarjeta
  3. ORM por anotaciones de especialización de Gol
  4. Modificar nuestro ObjectMapper como sea necesario
Paso 1: Vamos a crearnos dos de estas especificaciones: GolConId y TarjetaConId que heredarán de SucesoConId y además implementarán sus respectivas interfaces (Gol y Tarjeta). También vamos a crear los DAO correspondientes como lo hicimos en la sesión anterior sobre Spring Data Rest. Éste código no tiene ningún misterio y se puede ver fácilmente en el commit.

Como ya dijimos que para JPA las interfaces no existen, no caigamos en la tentación de asignar las definiciones a las interfaces Gol y Tarjeta (ver fichero Sucesos.orm.xml): deben ser clases que se puedan instanciar. De hecho podríamos tener implementaciones distintas a GolConId y TarjetaConId (y que tuvieran que guardarse de forma distinta también) lo que implicaría otro valor para discriminarlas. Como hay dos especificaciones voy a hacer ORM de formas distintas con cada una:

Paso 2: Tarjeta por XML (lo añado a SucesoConId.orm.xml para que se vea que puede haber varias entidades en el mismo fichero):
<entity class="es.lanyu.eventos.repositorios.SucesoConId" access="FIELD">
    <table name="SUCESOS"/>
    <!-- No hace falta strategy es el valor por defecto -->
    <inheritance strategy="SINGLE_TABLE"/>
    <discriminator-column name="TIPO"/>
    <discriminator-value>S</discriminator-value>
    <attributes>
        ...
    </attributes>
</entity>

<entity class="es.lanyu.eventos.repositorios.TarjetaConId" access="FIELD">
    <discriminator-value>T</discriminator-value>
    <attributes>
        <basic name="tipoTarjeta"/>
    </attributes>
</entity>
Paso 3: Y Gol lo haré por anotaciones añadiéndole lo que hace falta a la clase GolConId:
@Entity
@Access(value=AccessType.FIELD)
@DiscriminatorValue("G")
public class GolConId extends SucesoConId implements Gol { ... }
NOTA: ver @Access.

Paso 4: Ahora toca tunear nuestro MixIns para añadir y reutilizar código (implementación y extensión de ContadorDeMinutos y refactorizado de Datables-Partidos-Sucesos). Como no es parte de esta sesión mejor verlo en el commit o el video del webinar.

Con esto ya podemos levantar el servicio y empezar a añadir también goles y tarjetas. Veremos que crea URLs para los recursos /goles y /tarjetas donde podemos ver cada uno de ellos por separado o todos juntos en el recurso de /sucesos pero con relaciones agrupándo cada tipo distinto.

Se puede obtener el código hasta aquí en su repositorio. Se han añadido peticiones a la colección de Postman para generar goles y tarjetas aleatorias.

26 de marzo de 2020

ORM por XML con relaciones @OneToMany

Los dos casos vistos hasta ahora son entidades que sólo tienen campos básicos de tipo String, pero nuestros objetos de negocio muchas veces tienen relaciones con otros objetos con un campo de su tipo o incluso contienen una lista de ellos.

En esta entrada nos vamos a centrar en objetos que tengan una lista de entidades lo que forma una relación "Uno a Muchos" que en JPA se conoce con la anotación @OneToMany o la etiqueta one-to-many (y viceversa @ManyToOne).

La relación con @OneToMany puede hacerse con una Join Table o con una clave ajena en la tabla de entidades del tipo contenido en la lista (Join Column). En esta entrada se va a ver esto último.

Para verlo voy a usar la clase Partido de datos-deportivos, ya que tiene una Collection<Suceso> que hereda de EventoImpl.

Lo primero que tenemos que hacer es implementar un código necesario previo para centrarnos después en el objeto de esta entrada. El código está en el commit previo a empezar con la definición de estas relaciones y contiene:
  1. Creación de sendas clases entidad para Partido (PartidoConId) y Suceso (SucesoConId) ya que estos tipos no tienen ningún campo que pueda usarse como Id y todas las entidades lo necesitan.
  2. En este caso los Ids serán autogenerados por la base de datos con @GeneratedValue e IDENTITY.
  3. Como todos los tipos de los que heredan Partido y Suceso son de una librería de terceros tengo que usar la configuración XML porque no puedo anotar los campos.
  4. Una vez creados los ficheros ORM hay que añadirlos a la configuración.
Estos puntos es lo que hemos visto en las entradas anteriores y veremos que se parece bastante. Viendo el vídeo hay una explicación de este código.


Usando nuestros conocimientos de modelado de datos vamos a representar la relación PartidoConId-SucesoConId añadiendo a la tabla de Sucesos la clave ajena (FK) de PartidoConId al que pertenece ese Suceso.

Para ello definimos con la etiqueta one-to-many el nombre de nuestro campo con la colección de Sucesos, el tipo de entidad destino (SucesoConId) y con qué campo se mapea (con partido - veremos qué significa esto más abajo). Usamos la siguiente etiqueta dentro de los atributos de EventoImpl:
<one-to-many name="sucesos" target-entity="es.lanyu.eventos.repositorios.SucesoConId" mapped-by="partido"/>
NOTA: Es importante destacar que el tipo objetivo debe ser una entidad, no vale con la interface Suceso, debe ser la entidad SucesoConId.

Al hacerlo de esta forma (usando una FK en una Join Column) la relación debe ser bidireccional: se puede navegar de Partido a Suceso y viceversa. Esto implica que nuestra clase SucesoConId tiene que añadir un campo partido para saber cómo se mapea esa FK. Este campo debe tener el nombre que hemos puesto en mapped-by="partido" y ser del tipo de la entidad que contiene la colección (PartidoConId).
PartidoConId partido;

public PartidoConId getPartido() {
 return partido;
}

public void setPartido(PartidoConId partido) {
 this.partido = partido;
}
Además se deben añadir los respectivos accesores (getter/setter) para este campo. Esto se debe a que hay que mantener sincronizadas las dos entidades cuando se modifique (añadir en PartidoConId otro Suceso):
public void addSucesoConId(SucesoConId suceso) {
    super.addSuceso(suceso);
    suceso.setPartido(this);
}
Por último, debemos añadir en nuestro ORM para SucesoConId la etiqueta many-to-one para cerrar la relación en los dos sentidos e indicar la columna que se usa para hacer el join:
<many-to-one name="partido" optional="false"><!-- fetch="LAZY">-->
    <join-column name="ID_PARTIDO" referencedColumnName="ID"/>
</many-to-one>
NOTA: Dejo comentado el atributo fetch="LAZY" para ver cómo se pondría si se quisiera que la relación no se traiga los datos hasta que no se necesiten.

En el código hasta aquí se puede encontrar comentadas las anotaciones que corresponden a esta configuración XML en SucesoConId. Sin embargo, podemos ver que seríamos incapaces de añadírselo a EventoImpl al no ser código que gestionemos nosotros.

Todo esto puede verse funcionando en el video anterior con un main modificado que irá insertando partidos con un suceso y lo imprime para ver el resultado.

23 de marzo de 2020

ORM por XML de clases con herencia

En la entrada anterior vimos ORM por XML de una clase simple, sin relaciones y con campos de tipos básicos (String). En esta entrada vamos a ver cómo hacer ORM por XML de una clase con herencia usando la etiqueta mapped-superclass.

Para ello vamos a usar la clase Participante del proyecto datos-deportivos que es bastante simple aunque no es un POJO pues hereda de AbstractNombrable (y pertenece a otra librería). En definitiva se trata de usar la API de JPA para definir la relación entre los campos de nuestro objeto de negocio (incluidos los declarados en la superclase) y columnas de nuestras tablas de la BD.

Para ello hay que estudiar cómo se define la clase participante. No dispone de ningún campo propio: sólo tiene identificador y nombre pero pertenecen a las clases IdentificableString y AbstractNombrable respectivamente.

Vamos a añadir a nuestra carpeta resources/jpa los archivos IdentificableString.orm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm 
                                     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
                 version="1.0">

  <mapped-superclass class="es.lanyu.commons.identificable.IdentificableString"
                     access="FIELD">
    <attributes>
      <id name="id" />
    </attributes>
  </mapped-superclass>

</entity-mappings>
Y AbstractNombrable.orm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm 
                                     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
                 version="1.0">

  <mapped-superclass class="es.lanyu.commons.identificable.AbstractNombrable"
                     access="FIELD">
    <attributes>
      <basic name="nombre" optional="false" />
    </attributes>
  </mapped-superclass>

</entity-mappings>
De esta forma ya tenemos definido cómo se persisten las clases de las que hereda. En esta ocasión no se usa la etiqueta entity como se va a seguir usando en Participante sino que usamos mapped-superclass para las superclases que no son entidades. Con esta etiqueta vamos a definir el mapeo de todo lo que es común al resto de entidades que hereden. Si nos fijamos cada superclase tiene definidos sus propios campos.

Sólo nos quedaría añadir el archivo con al mapeo de Participante, Participante.orm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm 
                                     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
                 version="1.0">

  <entity class="es.lanyu.participante.Participante" access="FIELD">
    <table name="PARTICIPANTES"/>
    <!-- <attributes>
      <id name="id">
        <generated-value strategy="IDENTITY"/>
      </id>
      <basic name="nombre" optional="false">
        <column length="32"/>
      </basic>
    </attributes>-->
  </entity>

</entity-mappings>
Una vez definido el mapeo nos toca añadir el DAO correspondiente con las operaciones para Participante, con lo que nos creamos el sencillo PanticipanteDAO en el paquete es.lanyu.participante.repositorios. ¿Sabes crear el código por tí mismo?


package es.lanyu.participante.repositorios;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import es.lanyu.participante.Participante;

@Repository
public interface ParticipanteDAO extends JpaRepository {}

Y no hay que olvidarse de añadir el escaneo de este repositorio a nuestra configuración (añadimos otro elemento jpa:repositories). ¿Sabes dónde hay que añadirlo?



En el fichero resources/config/jpa-config.xml

Con esto ya estamos listos para realizar operaciones CRUD con participantes en nuestra BD.

Puedes encontrar el código hasta aquí en su repositorio y ver el video del webinar:

Te propongo como ejercicio que cargues todos los participantes que están en el fichero participantes.json en la BD programando lo que haga falta para leerlos (leyendo línea a línea cogiendo las que tienen datos y descartando las líneas de comentarios), persearlo con jackson (desde una línea con datos parsearla a Participante usando el bean ObjectMapper, ojo que hay un campo que habrá que ignorar - hashcode - usando un MixIn) y luego guardarlo con ParticipanteDAO.save. Finalmente imprime por el log con nivel TRACE los participantes que recuperes que tengan en su nombre la palabra "Real" (definir método con la query en DAO). Deben verse por consola (modificando el nivel de log correspondiente en propiedades).

Inténtalo a ver hasta donde llegas y, si no lo acabas o quieres compararla con otra solución, puedes ver el siguiente vídeo:

22 de marzo de 2020

ORM por XML de POJO simple

La entrada anterior vimos una persistencia muy básica sobre una clase propia que anotábamos. En nuestras aplicaciones usamos librerías de terceros y puede que debamos definir como se realizará la persistencia con una configuración externa al código de esa librería con lo que no se pueden usar anotaciones. En ese caso se puede:
  1. Usar Data Transfer Objects (DTO): POJOs que se utilicen para enviar información desde la capa de persistencia a la de servicio haciendo mapeos en ambos sentidos o
  2. Definir el mapeo de persistencia (ORM) del mismo tipo que se usa en el servicio usando configuración por XML.
En esta entrada empezamos a ver ORM por XML empezando por lo más sencillo: un POJO simple con campos de tipos básicos. Voy a usar la clase Usuario de la entrada anterior.

Lo primero es comentar las anotaciones que usamos para marcar la entidad (@Entity) y su clave primaria (@Id). En este momento si ejecutamos nos dirá que no existe una entidad gestionada para el tipo Usuario:

java.lang.IllegalArgumentException: Not a managed type: class es.lanyu.usuarios.repositorios.Usuario

Eso ocurre porque al intentar crear el bean para UsuarioDAO no es capaz de encontrar la definición de entidad para el tipo variable que le hemos pasado (Usuario):

Error creating bean with name 'usuarioDAO': Invocation of init method failed

Vamos entonces a definir por XML lo que hemos comentado. Lo haremos en un fichero con el nombre Usuario.orm.xml que pondremos en nuestros resources dentro de una carpeta nueva que llamaremos jpa:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm 
                                     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
                 version="1.0">

  <entity class="es.lanyu.usuarios.repositorios.Usuario" access="FIELD">
    <table name="USUARIOS"/>
    <attributes>
      <id name="nombre">
        <!-- <generated-value strategy="IDENTITY"/> -->
        <column length="16"/>
      </id>
      <basic name="correo" optional="false" />
    </attributes>
  </entity>
</entity-mappings>
Este fichero utiliza una definición que no habíamos usado hasta ahora y tiene que ver con JPA, no con Spring Framework. Ahí está definida la etiqueta entity.

Para nuestro caso identificamos la etiqueta entity y su atributo class con el valor de la clase Usuario con la anotación @Entity comentado.

Por otro lado identificamos la etiqueta id, y su atributo name con valor nombre, dentro de la etiqueta attributes con la anotación @Id comentada.

Esta información es necesaria igual que vimos en el caso de las anotaciones ya que para guardar un objeto debe ser de una clase de entidad y poseer un campo que sirva de clave primaria.

El resto de campos no son necesarios añadirlos ya que por defecto serán guardados en la BD si no están marcados como transient igual que pasaba con la serialización en Java nativo. Para esto y para añadir otra información de persistencia a un campo se utiliza la etiqueta basic. En nuestro caso lo añadimos para obligar que el campo tenga un valor con optional="false".
NOTA: Haz la prueba y mira en H2 cómo están definidos los cambios. Luego borrar la tabla (DROP TABLE USUARIOS) y ejecútalo comentando el campo correo y mira la diferencia en la tabla (NOT NULL vs NULL).

Fíjate que he puesto un elemento table indicando el nombre de la tabla (USUARIOS), de esta forma nos guardará los usuarios en esa nueva tabla y podremos compararla con la tabla USUARIO. Si no ponemos nada le pondrá el nombre de la entidad como pasó con las anotaciones y de ahí salió nuestra tabla USUARIO. La traducción a anotación de este elemento table es @Table.

Ahora tenemos que decirle a nuestra aplicación que lo incluya, para eso lo añadimos a nuestra configuración en XML. ¿Sabréis decir dónde está?



En el fichero resources/config/jpa-config.xml

Si revisamos por encima el código enseguida vemos dónde se indican estos archivos de mapeo. Simplemente añadiendo el nuevo ya estará todo listo para que Spring coja la nueva configuración y pueda empezar a guardar usuarios de nuevo.

Puedes encontrar el código hasta aquí en su repositorio y ver el vídeo del webinar:

En la siguiente entrada veremos cómo hacer esto con clases que tienen superclases.

19 de marzo de 2020

Entidades y Repositorios con JPA

Ya sabemos que nuestros datos deben ser persistidos para poder ser recuperados al arrancar nuestro sistema, ser compartidos, almacenados para análisis por parte de otras herramientas, etc...

Hasta ahora hemos visto cómo serializar nuestros datos y guardarlos en ficheros de nuestro disco. Si tenemos una gran cantidad de datos sería bueno que en vez de leer todos los datos y tratarlos en nuestra aplicación para seleccionar el que de verdad necesito (a lo mejor un cliente de entre millones) sería bueno usar un Sistema Gestor de Base de Datos (SGBD), que aparte de ofrecerme las típicas operaciones SQL puedo consumirlo como un servicio que atienda muchas peticiones a la vez. En cualquier caso, está claro que no vale simplemente con guardar en un fichero unos cuantos datos y que Spring tiene su solución.

Me voy a centrar en las Bases de Datos Relacionales ya que JPA está pensado para estas (quedan fuera del contenido las BD NoSQL)

¿Qué es JPA?

JPA es el acrónimo de Java Persistence API lo cual deja bastante claro de qué se ocupa: Es una API para Persistencia en Java. Para saber cómo utilizarlo hay un wikibook que explica bastante clara y concisamente su uso proporcionando ejemplos en las dos formas distintas que hay de definir cómo ejecutar la persistencia de nuestros datos.

JPA es la definición de una API pero no una implementación. De la misma forma que vimos en la entrada de logging, usamos una API que nos proporciona una fachada frente a sus implementaciones y de esta forma es sencillo cambiar la implementación usada por la aplicación. Existen varias implementaciones:
Visitando sus páginas se puede ver que dan soporte a muchos SGBD distintos, incluidos NoSQL. Hay que tener en cuenta que estas implementaciones cumplen JPA pero no son sólo eso.

Lo normal es que este tipo de herramientas proporcionen un mapeo entre objetos de aplicación y registros en SGBD relacional. De esta forma la aplicación es capaz de volcar nuestros datos en memoria a una BD y viceversa. Esto se conoce como Object-Relacional Mapping (ORM) y veremos su uso más adelante.

El sentido de tener JPA, ORM, Hibernate, etc... es que hay también mucho código boilerplate y es muy laborioso de implementar, probar y repetitivo. Sirva como ejemplo ese snipet básico con JDBC de una implementación para el supuesto con el que vamos a trabajar: tiene cientos de líneas de código, hace bastante poco y está muy ligado a un SGBD concreto.

Usando JPA vamos a persistir nuestros datos en cualquier SGBD con muy poco código, de manera fiable y con una facilidad increíble de cambiar de SGBD sin tener que modificar el código. Para guardar una entidad podremos hacerlo simplemente con dos anotaciones sobre ella y declarar una interface que extienda otra con una anotación sobre ella. Voy a usar un POJO simple para una clase Usuario que pondré, para simplificar este ejemplo, en el paquete es.lanyu.usuarios.repositorios pero no es obligatorio:
@Entity
public class Usuario {

    @Id
//  @GeneratedValue
//  int id;

    String nombre;

    String correo;

}
Simplemente las anotaciones @Entity y @Id definen que esta clase es una entidad y que su clave principal es el nombre de usuario.

Para guardar la entidad Usuario hay que crear un Repositorio que tendrá las operaciones CRUD típicas. Será mi interface UsuarioDAO y lo pongo en el mismo paquete que Usuario:
@Repository
public interface UsuarioDAO extends JpaRepository {}
Con sólo estas dos líneas tengo todas las operaciones CRUD y además otras cosas como paginación y consultas personalizadas.

Si hacemos memoria, cuando vimos @Component había tres especializaciones de ella, aquí estamos usando una de ellas: @Repository.

Al marcar esta interface con esa anotación la estamos haciendo autodetectable y puede ser escaneada para ser añadida como un bean.

Si ejecutamos el código en este punto no hará nada, de hecho ni siquiera será escaneado. Nos falta el encargado de hacer que todo esto funcione, que sepa con que BD debe conectar, las credenciales para hacerlo, la implementación a usar y otras configuraciones necesarias. El responsable de todo esto en JPA se llama EntityManager.

Hay varias formas de crearlo. Nosotros vamos a usar XML y voy a incluir también dónde están nuestros repositorios de JPA. Si se nos pasa el momento de susto al ver un XML con muchas cosas que no entendemos y nos centramos en lo importante, veremos que es prácticamente un copy & paste de este snipet.


NOTA: Estoy usando la capacidad que tenemos de configurar con propiedades el XML para mejorar la reutilización y simplificarlo, pero podría estar escrito el valor directamente.

Sólo tenemos que establecer nuestras propiedades para decir dónde están nuestros repositorios y nuestras entidades:
es.lanyu.entities-package=es.lanyu.usuarios.repositorios
es.lanyu.jpa-package=${es.lanyu.entities-package}
Ya dijimos que con Spring Boot eliminamos la necesidad de usar XML. La traducción a anotación de estas dos informaciones se puede hacer sobre nuestra clase de configuración:
Nos creamos un usuario para ver que todo funciona correctamente y se nos guarda. Para simplificar las pruebas lo genero en automático así que queda nuestro código del main así:
public static void main(String[] args) {
    ConfigurableApplicationContext context =
            SpringApplication.run(DatosdeportivosapiApplication.class, args);

    UsuarioDAO usuarioDAO = context.getBean(UsuarioDAO.class);
    usuarioDAO.save(generaUsuario());
    List<Usuario> usuarios = usuarioDAO.findAll();
    usuarios.stream().map(Usuario::toString).forEach(log::info);

    context.close();
}

static Usuario generaUsuario() {
    int numero = 10000;
    String usuario = "user" + ThreadLocalRandom.current().nextInt(numero, numero*20);
    return new Usuario(usuario, usuario + "@mail.com");
}

¿Qué operaciones tiene mi repositorio?

Tiene las que se pueden esperar de un CRUD. Usando el asistente de contenido no hacemos una idea. Lo bueno de es que JPA tiene una sintaxis para el nombre de sus métodos que implica un mapeo a una sentencia SQL sin tener que implementarla. El siguiente diagrama lo describe:


Fuente: Libro Spring in Action

Podemos ver las palabras clave y posibilidades de esta sintaxis en la documentación.

En nuestro caso vamos a recuperar todos los usuarios que contengan un texto que digamos en su correo:
List<Usuario> findByCorreoContaining(String txt);
Si usamos este nuevo método, reemplazando al findAll() anterior, podemos ver que nos devuelve ya sólo los registros que coincidan:
List<Usuario> usuarios = usuarioDAO.findByCorreoContaining("5");
Todavía queda mucho para dominar JPA, pero creo que si comparamos el ejemplo usando JDBC a usando JPA la diferencia es abismal y merece la pena el esfuerzo de aprenderlo y usarlo.

Puedes ver el código hasta este punto en su repositorio.

Lo siguiente que veremos es cómo hacer ese mapeo ORM sobre entidades a las que no tenemos acceso al código.

Compárteme

Entradas populares