Wat is JPA? Inleiding tot de Java Persistence API

Als specificatie houdt de Java Persistence API zich bezig met persistentie , wat losjes betekent elk mechanisme waarmee Java-objecten het applicatieproces overleven dat ze heeft gemaakt. Niet alle Java-objecten hoeven te worden bewaard, maar de meeste applicaties blijven belangrijke bedrijfsobjecten behouden. Met de JPA-specificatie kunt u bepalen welke objecten moeten worden behouden en hoe die objecten moeten worden behouden in uw Java-toepassingen.

Op zichzelf is PPV geen tool of raamwerk; het definieert eerder een reeks concepten die door elk hulpmiddel of raamwerk kunnen worden geïmplementeerd. Hoewel het object-relationele mapping (ORM) -model van JPA oorspronkelijk was gebaseerd op Hibernate, is het sindsdien geëvolueerd. Evenzo, terwijl JPA oorspronkelijk bedoeld was voor gebruik met relationele / SQL-databases, zijn sommige JPA-implementaties uitgebreid voor gebruik met NoSQL-datastores. Een populair framework dat JPA met NoSQL ondersteunt, is EclipseLink, de referentie-implementatie voor JPA 2.2.

JPA 2.2 in Jakarta EE

De Java Persistence API werd voor het eerst uitgebracht als een subset van de EJB 3.0-specificatie (JSR 220) in Java EE 5. Het is sindsdien geëvolueerd als zijn eigen specificatie, te beginnen met de release van JPA 2.0 in Java EE 6 (JSR 317). Op het moment van schrijven is JPA 2.2 aangenomen voor voortzetting als onderdeel van Jakarta EE.

JPA en slaapstand

Vanwege hun met elkaar verweven geschiedenis worden Hibernate en JPA vaak samengevoegd. Net als de Java Servlet-specificatie heeft JPA echter veel compatibele tools en frameworks voortgebracht; Hibernate is er slechts een van.

Hibernate is ontwikkeld door Gavin King en begin 2002 uitgebracht en is een ORM-bibliotheek voor Java. King ontwikkelde Hibernate als alternatief voor Entity Beans voor persistentie. Het raamwerk was zo populair en zo nodig in die tijd dat veel van zijn ideeën werden overgenomen en gecodificeerd in de eerste PPV-specificatie.

Tegenwoordig is Hibernate ORM een van de meest volwassen JPA-implementaties en nog steeds een populaire optie voor ORM in Java. Hibernate ORM 5.3.8 (de huidige versie op het moment van schrijven) implementeert JPA 2.2. Bovendien is de reeks tools van Hibernate uitgebreid met populaire tools zoals Hibernate Search, Hibernate Validator en Hibernate OGM, die de persistentie van domeinmodellen voor NoSQL ondersteunen.

JPA en EJB

Zoals eerder opgemerkt, werd JPA geïntroduceerd als een subset van EJB 3.0, maar is sindsdien geëvolueerd als zijn eigen specificatie. EJB is een specificatie met een andere focus dan JPA, en is geïmplementeerd in een EJB-container. Elke EJB-container bevat een persistentielaag, die wordt gedefinieerd door de JPA-specificatie.

Wat is Java ORM?

Hoewel ze verschillen in uitvoering, biedt elke JPA-implementatie een soort ORM-laag. Om JPA en JPA-compatibele tools te begrijpen, moet u ORM goed beheersen.

Object-relationele mapping is een taak - een taak die ontwikkelaars goede redenen hebben om deze niet handmatig te doen. Een framework zoals Hibernate ORM of EclipseLink codeert die taak in een bibliotheek of framework, een ORM-laag . Als onderdeel van de applicatiearchitectuur is de ORM-laag verantwoordelijk voor het beheer van de conversie van software-objecten voor interactie met de tabellen en kolommen in een relationele database. In Java converteert de ORM-laag Java-klassen en -objecten zodat ze kunnen worden opgeslagen en beheerd in een relationele database.

Standaard wordt de naam van het object dat wordt bewaard, de naam van de tabel en worden velden kolommen. Zodra de tabel is opgesteld, komt elke tabelrij overeen met een object in de applicatie. Objecttoewijzing is configureerbaar, maar standaardinstellingen werken meestal goed.

JPA met NoSQL

Tot voor kort waren niet-relationele databases ongebruikelijke curiosa. De NoSQL-beweging veranderde dat allemaal en nu zijn er verschillende NoSQL-databases beschikbaar voor Java-ontwikkelaars. Sommige JPA-implementaties zijn geëvolueerd om NoSQL te omarmen, waaronder Hibernate OGM en EclipseLink.

Figuur 1 illustreert de rol van JPA en de ORM-laag in applicatieontwikkeling.

JavaWorld /

De Java ORM-laag configureren

Wanneer u een nieuw project opzet om JPA te gebruiken, moet u de datastore en JPA-provider configureren. Je configureert een datastore-connector om verbinding te maken met de door jou gekozen database (SQL of NoSQL). Je voegt ook de JPA-provider toe en configureert deze , een framework zoals Hibernate of EclipseLink. Hoewel u JPA handmatig kunt configureren, kiezen veel ontwikkelaars ervoor om de out-of-the-box-ondersteuning van Spring te gebruiken. Zie " Installatie en configuratie van JPA " hieronder voor een demonstratie van zowel handmatige als Spring-gebaseerde JPA-installatie en configuratie.

Java-gegevensobjecten

Java Data Objects is een gestandaardiseerd persistentieframework dat voornamelijk verschilt van JPA door ondersteuning van persistentielogica in het object en door de langdurige ondersteuning voor het werken met niet-relationele datastores. JPA en JDO lijken genoeg op elkaar dat JDO-providers vaak ook JPA ondersteunen. Zie het Apache JDO-project voor meer informatie over JDO in relatie tot andere persistentiestandaarden zoals JPA en JDBC.

Gegevenspersistentie in Java

Vanuit programmeerperspectief is de ORM-laag een adapterlaag : hij past de taal van objectgrafieken aan de taal van SQL en relationele tabellen aan. De ORM-laag stelt objectgeoriënteerde ontwikkelaars in staat software te bouwen die gegevens vasthoudt zonder ooit het objectgeoriënteerde paradigma te verlaten.

Wanneer u JPA gebruikt, maakt u een kaart van de datastore naar de datamodelobjecten van uw toepassing. In plaats van te definiëren hoe objecten worden opgeslagen en opgehaald, definieert u de toewijzing tussen objecten en uw database en roept u vervolgens JPA op om ze te behouden. Als u een relationele database gebruikt, wordt een groot deel van de daadwerkelijke verbinding tussen uw applicatiecode en de database afgehandeld door JDBC, de Java Database Connectivity API.

Als specificatie biedt JPA metadata-annotaties , die u gebruikt om de toewijzing tussen objecten en de database te definiëren. Elke JPA-implementatie biedt zijn eigen engine voor JPA-annotaties. De JPA-specificatie biedt ook de PersistanceManagerof EntityManager, wat de belangrijkste contactpunten zijn met het JPA-systeem (waarin uw bedrijfslogica het systeem vertelt wat het met de in kaart gebrachte objecten moet doen).

Overweeg om dit allemaal concreter te maken Listing 1, een eenvoudige dataklasse voor het modelleren van een muzikant.

Listing 1. Een eenvoudige dataklasse in 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... } 

De Musicianklasse in Listing 1 wordt gebruikt om gegevens vast te houden. Het kan primitieve data bevatten, zoals de naam van het veld. Het kan ook relaties hebben met andere klassen, zoals mainInstrumenten 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.

Tot slot Musiciankan een hebben één-op-één relatie met een Quoteentiteit, wordt gebruikt om een beroemd citaat vertegenwoordigen: Quote famousQuote = new Quote().

Relatietypes definiëren

JPA heeft annotaties voor elk van de typen relatietoewijzingen. Lijst 7 laat zien hoe u de een-op-veel-relatie tussen Musicianen Performances kunt annoteren .

Lijst 7. Een een-op-veel-relatie annoteren