¿Qué es JPA? Introducción a la API de persistencia de Java

Como especificación, la API de persistencia de Java se ocupa de la persistencia , lo que significa libremente cualquier mecanismo mediante el cual los objetos de Java sobreviven al proceso de aplicación que los creó. No es necesario conservar todos los objetos Java, pero la mayoría de las aplicaciones conservan objetos comerciales clave. La especificación JPA le permite definir qué objetos deben persistir y cómo esos objetos deben persistir en sus aplicaciones Java.

Por sí mismo, JPA no es una herramienta o un marco; más bien, define un conjunto de conceptos que pueden ser implementados por cualquier herramienta o marco. Si bien el modelo de mapeo relacional de objetos (ORM) de JPA se basó originalmente en Hibernate, desde entonces ha evolucionado. Del mismo modo, aunque JPA se diseñó originalmente para su uso con bases de datos relacionales / SQL, algunas implementaciones de JPA se han ampliado para su uso con almacenes de datos NoSQL. Un marco popular que admite JPA con NoSQL es EclipseLink, la implementación de referencia para JPA 2.2.

JPA 2.2 en Yakarta EE

La API de persistencia de Java se lanzó por primera vez como un subconjunto de la especificación EJB 3.0 (JSR 220) en Java EE 5. Desde entonces ha evolucionado como su propia especificación, comenzando con el lanzamiento de JPA 2.0 en Java EE 6 (JSR 317). En el momento de escribir este artículo, se adoptó la JPA 2.2 para su continuación como parte de Jakarta EE.

JPA e hibernación

Debido a su historia entrelazada, Hibernate y JPA se combinan con frecuencia. Sin embargo, al igual que la especificación Java Servlet, JPA ha generado muchas herramientas y marcos compatibles; Hibernate es solo uno de ellos.

Desarrollado por Gavin King y lanzado a principios de 2002, Hibernate es una biblioteca ORM para Java. King desarrolló Hibernate como una alternativa a los beans de entidad para la persistencia. El marco era tan popular, y tan necesario en ese momento, que muchas de sus ideas fueron adoptadas y codificadas en la primera especificación de JPA.

Hoy, Hibernate ORM es una de las implementaciones de JPA más maduras y sigue siendo una opción popular para ORM en Java. Hibernate ORM 5.3.8 (la versión actual al momento de escribir este artículo) implementa JPA 2.2. Además, la familia de herramientas de Hibernate se ha expandido para incluir herramientas populares como Hibernate Search, Hibernate Validator e Hibernate OGM, que admite la persistencia del modelo de dominio para NoSQL.

JPA y EJB

Como se señaló anteriormente, JPA se introdujo como un subconjunto de EJB 3.0, pero desde entonces ha evolucionado como su propia especificación. EJB es una especificación con un enfoque diferente de JPA y se implementa en un contenedor EJB. Cada contenedor EJB incluye una capa de persistencia, que está definida por la especificación JPA.

¿Qué es Java ORM?

Si bien difieren en la ejecución, cada implementación de JPA proporciona algún tipo de capa ORM. Para comprender las herramientas compatibles con JPA y JPA, debe tener un buen conocimiento de ORM.

El mapeo relacional de objetos es una tarea, una que los desarrolladores tienen buenas razones para evitar realizar manualmente. Un marco como Hibernate ORM o EclipseLink codifica esa tarea en una biblioteca o marco, una capa ORM . Como parte de la arquitectura de la aplicación, la capa ORM es responsable de administrar la conversión de objetos de software para interactuar con las tablas y columnas en una base de datos relacional. En Java, la capa ORM convierte las clases y los objetos de Java para que puedan almacenarse y administrarse en una base de datos relacional.

De forma predeterminada, el nombre del objeto que se persiste se convierte en el nombre de la tabla y los campos se convierten en columnas. Una vez configurada la tabla, cada fila de la tabla corresponde a un objeto en la aplicación. El mapeo de objetos es configurable, pero los valores predeterminados tienden a funcionar bien.

JPA con NoSQL

Hasta hace relativamente poco tiempo, las bases de datos no relacionales eran curiosidades poco comunes. El movimiento NoSQL cambió todo eso, y ahora hay una variedad de bases de datos NoSQL disponibles para los desarrolladores de Java. Algunas implementaciones de JPA han evolucionado para adoptar NoSQL, incluidas Hibernate OGM y EclipseLink.

La Figura 1 ilustra el papel de JPA y la capa ORM en el desarrollo de aplicaciones.

JavaWorld /

Configuración de la capa ORM de Java

Cuando configure un nuevo proyecto para usar JPA, deberá configurar el almacén de datos y el proveedor de JPA. Configurará un conector de almacén de datos para conectarse a la base de datos elegida (SQL o NoSQL). También incluirá y configurará el proveedor JPA , que es un marco como Hibernate o EclipseLink. Si bien puede configurar JPA manualmente, muchos desarrolladores optan por utilizar el soporte listo para usar de Spring. Consulte " Instalación y configuración de JPA " a continuación para ver una demostración de la instalación y configuración de JPA tanto manual como basada en Spring.

Objetos de datos Java

Java Data Objects es un marco de persistencia estandarizado que se diferencia de JPA principalmente por admitir la lógica de persistencia en el objeto y por su soporte de larga data para trabajar con almacenes de datos no relacionales. JPA y JDO son lo suficientemente similares como para que los proveedores de JDO con frecuencia también admitan JPA. Consulte el Proyecto Apache JDO para obtener más información sobre JDO en relación con otros estándares de persistencia como JPA y JDBC.

Persistencia de datos en Java

Desde una perspectiva de programación, la capa ORM es una capa adaptadora : adapta el lenguaje de los gráficos de objetos al lenguaje de SQL y tablas relacionales. La capa ORM permite a los desarrolladores orientados a objetos crear software que conserva los datos sin abandonar el paradigma orientado a objetos.

Cuando usa JPA, crea un mapa desde el almacén de datos a los objetos del modelo de datos de su aplicación. En lugar de definir cómo se guardan y recuperan los objetos, usted define el mapeo entre los objetos y su base de datos y luego invoca JPA para conservarlos. Si está utilizando una base de datos relacional, gran parte de la conexión real entre el código de su aplicación y la base de datos será manejada por JDBC, la API de conectividad de base de datos de Java.

Como especificación, JPA proporciona anotaciones de metadatos , que se utilizan para definir el mapeo entre los objetos y la base de datos. Cada implementación de JPA proporciona su propio motor para las anotaciones de JPA. La especificación JPA también proporciona PersistanceManagero EntityManager, que son los puntos clave de contacto con el sistema JPA (donde su código de lógica empresarial le dice al sistema qué hacer con los objetos mapeados).

Para hacer todo esto más concreto, considere el Listado 1, que es una clase de datos simple para modelar a un músico.

Listado 1. Una clase de datos simple en Java

 public class Musician { private Long id; private String name; private Instrument mainInstrument; private ArrayList performances = new ArrayList(); public Musician( Long id, String name){ /* constructor setters... */ } public void setName(String name){ this.name = name; } public String getName(){ return this.name; } public void setMainInstrument(Instrument instr){ this.instrument = instr; } public Instrument getMainInstrument(){ return this.instrument; } // ...Other getters and setters... } 

La Musicianclase del Listado 1 se utiliza para almacenar datos. Puede contener datos primitivos como el campo de nombre . También puede mantener relaciones con otras clases como mainInstrumenty performances.

Musician's reason for being is to contain data. This type of class is sometimes known as a DTO, or data transfer object. DTOs are a common feature of software development. While they hold many kinds of data, they do not contain any business logic. Persisting data objects is a ubiquitous challenge in software development.

Data persistence with JDBC

One way to save an instance of the Musician class to a relational database would be to use the JDBC library. JDBC is a layer of abstraction that lets an application issue SQL commands without thinking about the underlying database implementation.

Listing 2 shows how you could persist the Musician class using JDBC.

Listing 2. JDBC inserting a record

 Musician georgeHarrison = new Musician(0, "George Harrison"); String myDriver = "org.gjt.mm.mysql.Driver"; String myUrl = "jdbc:mysql://localhost/test"; Class.forName(myDriver); Connection conn = DriverManager.getConnection(myUrl, "root", ""); String query = " insert into users (id, name) values (?, ?)"; PreparedStatement preparedStmt = conn.prepareStatement(query); preparedStmt.setInt (1, 0); preparedStmt.setString (2, "George Harrison"); preparedStmt.setString (2, "Rubble"); preparedStmt.execute(); conn.close(); // Error handling removed for brevity 

The code in Listing 2 is fairly self-documenting. The georgeHarrison object could come from anywhere (front-end submit, external service, etc.), and has its ID and name fields set. The fields on the object are then used to supply the values of an SQL insert statement. (The PreparedStatement class is part of JDBC, offering a way to safely apply values to an SQL query.)

While JDBC allows the control that comes with manual configuration, it is cumbersome compared to JPA. In order to modify the database, you first need to create an SQL query that maps from your Java object to the tables in a relational database. You then have to modify the SQL whenever an object signature change. With JDBC, maintaining the SQL becomes a task in itself.

Data persistence with JPA

Now consider Listing 3, where we persist the Musician class using JPA.

Listing 3. Persisting George Harrison with JPA

 Musician georgeHarrison = new Musician(0, "George Harrison"); musicianManager.save(georgeHarrison); 

Listing 3 replaces the manual SQL from Listing 2 with a single line, session.save(), which instructs JPA to persist the object. From then on, the SQL conversion is handled by the framework, so you never have to leave the object-oriented paradigm.

Metadata annotations in JPA

The magic in Listing 3 is the result of a configuration, which is created using JPA's annotations. Developers use annotations to inform JPA which objects should be persisted, and how they should be persisted.

Listing 4 shows the Musician class with a single JPA annotation.

Listing 4. JPA's @Entity annotation

 @Entity public class Musician { // ..class body } 

Persistent objects are sometimes called entities. Attaching @Entity to a class like Musician informs JPA that this class and its objects should be persisted.

XML vs. annotation-based configuration

JPA also supports using external XML files, instead of annotations, to define class metadata. But why would you do that to yourself?

Configuring JPA

Like most modern frameworks, JPA embraces coding by convention (also known as convention over configuration), in which the framework provides a default configuration based on industry best practices. As one example, a class named Musician would be mapped by default to a database table called Musician.

The conventional configuration is a timesaver, and in many cases it works well enough. It is also possible to customize your JPA configuration. As an example, you could use JPA's @Table annotation to specify the table where the Musician class should be stored.

Listing 5. JPA's @Table annotation

 @Entity @Table(name="musician") public class Musician { // ..class body } 

Listing 5 tells JPA to persist the entity (Musician class) to the musician table.

Primary key

In JPA, the primary key is the field used to uniquely identify each object in the database. The primary key is useful for referencing and relating objects to other entities. Whenever you store an object in a table, you will also specify the field to use as its primary key.

In Listing 6, we tell JPA what field to use as Musician's primary key.

Listing 6. Specifying the primary key

 @Entity public class Musician { @Id private Long id; 

In this case, we've used JPA's @Id annotation to specify the id field as Musician's primary key. By default, this configuration assumes the primary key will be set by the database--for instance, when the field is set to auto-increment on the table.

JPA supports other strategies for generating an object's primary key. It also has annotations for changing individual field names. In general, JPA is flexible enough to adapt to any persistence mapping you might need.

CRUD operations

Once you've mapped a class to a database table and established its primary key, you have everything you need to create, retrieve, delete, and update that class in the database. Calling session.save() will create or update the specified class, depending on whether the primary-key field is null or applies to en existing entity. Calling entityManager.remove() will delete the specified class.

Entity relationships in JPA

Simply persisting an object with a primitive field is only half the equation. JPA also has the capability to manage entities in relation to one another. Four kinds of entity relationships are possible in both tables and objects:

    1. One-to-many
    2. Many-to-one
    3. Many-to-many
    4. One-to-one

Each type of relationship describes how an entity relates to other entities. For example, the Musician entity could have a one-to-many relationship with Performance, an entity represented by a collection such as List or Set.

If the Musician included a Band field, the relationship between these entities could be many-to-one, implying collection of Musicians on the single Band class. (Assuming each musician only performs in a single band.)

If Musician included a BandMates field, that could represent a many-to-many relationship with other Musician entities.

Por último, Musicianpodría tener una relación de uno a uno con una Quoteentidad, que se utiliza para representar una famosa cita: Quote famousQuote = new Quote().

Definición de tipos de relación

JPA tiene anotaciones para cada uno de sus tipos de mapeo de relaciones. El Listado 7 muestra cómo podría anotar la relación de uno a muchos entre Musiciany Performances.

Listado 7. Anotar una relación de uno a varios