11.5 C
New York
Tuesday, October 21, 2025
Array

What is JPA? Introduction to Java persistence



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 entityManager.save() will create or update the specified class, depending on whether the primary-key field is null or applies to an existing entity. Calling entityManager.remove() will delete the specified class.

Entity relationships

Simply persisting an object with a primitive field is only half the equation. JPA also lets you 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 a 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. (In this case the musician rows/objects are self-referencing, another common pattern.)

Finally, Musician might have a one-to-one relationship with a Quote entity, used to represent a famous quote: Quote famousQuote = new Quote().

Defining relationship types

JPA has annotations for each of its relationship mapping types. The following code shows how you might annotate the one-to-many relationship between Musician and Performances. In this case, each musician might have many performances, but there is only one musician for each performance:


// Performance.java
@Entity
public class Performance {
    @Id
    @GeneratedValue
    private Long id;
    private String title; // e.g., "Live at Abbey Road"

    // Many Performances belong to one Musician
    // @JoinColumn specifies the foreign key column in the 'Performance' table
    @ManyToOne
    @JoinColumn(name = "musician_id") // This will be the FK column in the 'performance' table
    private Musician musician;

   // constructor and members...
}

public class Musician {
  @OneToMany(mappedBy = "musician")
  private List performances = new ArrayList();
  //...
}

Notice that the @JoinColumn tells JPA what column on the Performance table will map to the Musician entity. Each Performance will be associated with a single Musician, which is tracked by this column. When JPA loads a Musician or Performance object into the database, it will use this information to reconstitute the object graph.

For Musician, the @OneToMany(mappedBy = 'musician') annotation tells JPA to use the Performance.musician field to populate the performances List on the Musician object. (That is, the Performance.musician field points from the Performance table to the Musician table.)

When JPA loads the foreign key from Performance, it will populate the actual Musician object found at that primary key in the Musician table, and the live List of performances hydrated by the performances holding those foreign keys. As a result, the performances are loaded holding a reference to the Musician objects, and these objects are loaded holding Lists of the performances.

There is more we can do to fine-tune how these relationships work. Right now, we’re just touching on the basics.

Also see: Java persistence with JPA and Hibernate: Entities and relationships.

JPA fetching strategies

In addition to knowing where to place related entities in the database, JPA needs to know how you want them loaded. Fetching strategies tell JPA how to load related entities. When loading and saving objects, a JPA framework must provide the ability to finetune how object graphs are handled. For instance, if the Musician class has a bandMate field, loading GeorgeHarrison could cause the entire Musician table to be loaded from the database!

You can use annotations to customize your fetching strategies, but JPA’s default configuration often works out of the box:

  1. One-to-many: Lazy
  2. Many-to-one: Eager
  3. Many-to-many: Lazy
  4. One-to-one: Eager

Transactions in JPA

While outside the scope of this introduction, transactions allow the developer to define boundaries for groups of operations to the database. We can define several operations together and then execute them together with entityManager.getTransaction().commit(). If any of the related operations fails, the whole transaction will rollback. This is another essential component of data design.

Transactions can be defined in a variety of ways, from explicit interactions via the API, to using annotations to define transactional boundaries, to using Spring AOP to define the boundaries.

JPA installation and setup

We’ll conclude with a quick look at installing and setting up JPA for your Java applications. For this demonstration we will use EclipseLink, the JPA reference implementation.

The common way to install JPA is to include a JPA provider into your project:


<dependency>
	<groupId>org.eclipse.persistence</groupId>
	<artifactId>eclipselink</artifactId>
	<version>4.0.7</version>
</dependency>

We also need to include a database driver:


<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.33</version>
</dependency>

Then, we need to tell the system about our database and provider, which we do in a persistence.xml file:


http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="MyUnit" transaction-type="RESOURCE_LOCAL">
    <properties>
      <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/foo_bar"/>
      <property name="jakarta.persistence.jdbc.user" value=""/>
      <property name="jakarta.persistence.jdbc.password" value=""/>
      <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
    </properties>
  </persistence-unit>
</persistence>

There are other ways to provide this information to the system, including programmatically. I recommend using the persistence.xml file because storing dependencies this way makes it very easy to update your application without modifying any code.

Spring configuration for JPA

Using Spring Data JPA will greatly ease the integration of JPA into your application. As an example, placing the @SpringBootApplication annotation in your application header instructs Spring to automatically scan for classes and inject the EntityManager as required, based on the configuration you’ve specified.

Include the following dependencies in your build if you want Spring’s JPA support for your application:


<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<version>3.5.3</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
	<version>3.5.3</version>
</dependency>

When to use JPA

The question of whether to use JPA is a common source of analysis paralysis when designing a Java application. Especially when attempting to make up-front technology decisions, you don’t want to get data persistence—an essential and long-term factor—wrong.

To break this kind of paralysis, it’s useful to remember that applications can evolve into using JPA. You might build exploratory or prototype code using JDBC, then start adding in JPA. There’s no reason these solutions can’t coexist.

After being paralyzed by indecision, the next worst thing is to adopt JPA when the additional effort it implies will prevent a project from moving forward. JPA can be a win for overall system stability and maintainability, but sometimes simpler is better, especially at the beginning of a project. If your team doesn’t have the capacity to adopt JPA up front, consider putting it on your roadmap for the future.

Conclusion

Every application that interfaces with a database should define an application layer whose sole purpose is to isolate persistence code. As you’ve seen in this article, the Jakarta Persistence API (JPA) introduces a range of capabilities and support for Java object persistence. Simple applications may not require every JPA capability, and in some cases the overhead of configuring the framework may not be merited. As an application grows, however, JPA really earns its keep. Using JPA keeps your object code simple and provides a conventional framework for accessing data in Java applications.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Stay Connected

0FansLike
0FollowersFollow
0FollowersFollow
0SubscribersSubscribe
- Advertisement -spot_img

CATEGORIES & TAGS

- Advertisement -spot_img

LATEST COMMENTS

Most Popular

WhatsApp