Week 139 — What is JPA (Jakarta Persistence)?

Question of the Week #139
What is JPA (Jakarta Persistence)?
7 Replies
JavaObi
JavaObi3w ago
The Jakarta Persistence API (JPA) is a specification for object-relational mapping and it is part of Jakarta EE. Object-relational mapping refers to the persistence of objects (in an OOP language like Java) into a relational (SQL) database. As such, JPA is responsible for storing objects in a database as well as retrieving and modifying them. JPA is based on entities which are objects that can be represented in the database. Entity classes are typically annotated with @Entity and contain an @Id field containing the primary key. For example, consider the following class:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // the ID is generated automatically by the database
private Long id;

private String name;
private int age;

// getters/setters omitted in this example
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // the ID is generated automatically by the database
private Long id;

private String name;
private int age;

// getters/setters omitted in this example
}
This class corresponds to a table created with an SQL statement similar to the following (the exact DDL is dependent on the database):
CREATE TABLE person (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, age INT NOT NULL);
CREATE TABLE person (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, age INT NOT NULL);
It is possible to use additional annotations to customize the representation of the entity in the database:
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "p_name")
private String name;
private int age;

// getters/setters omitted in this example
}
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "p_name")
private String name;
private int age;

// getters/setters omitted in this example
}
JPA would then expect the tables and columns to be named accordingly:
CREATE TABLE people (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, p_name VARCHAR(255) NOT NULL, age INT NOT NULL);
CREATE TABLE people (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, p_name VARCHAR(255) NOT NULL, age INT NOT NULL);
After configuring a JPA implementation (like EclipseLink or Hibernate) for a database containing the relevant tables and obtaining an EntityManager, it can be used to persist objects of that class. An EntityManager can be obtained either using an EntityManagerFactory or using dependency injection:
private EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");

void doSomething(){
try(EntityManager em = emf.createEntityManager()) {
em.getTransaction().begin();

// use em to access the database

em.getTransaction().commit();
}catch(RuntimeException e) {
em.getTransaction().rollback();
throw e;
}
// alternatively
emf.runInTransaction(em -> {
// use em to access the database
});
}
private EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence-unit");

void doSomething(){
try(EntityManager em = emf.createEntityManager()) {
em.getTransaction().begin();

// use em to access the database

em.getTransaction().commit();
}catch(RuntimeException e) {
em.getTransaction().rollback();
throw e;
}
// alternatively
emf.runInTransaction(em -> {
// use em to access the database
});
}
// in managed environments, dependency injection can be used instead:
@Stateless
public class SomeBean {
@PersistenceContext
private EntityManager em;

@Transactional
public void doSomething() {
// use em to access the database
}
}
// in managed environments, dependency injection can be used instead:
@Stateless
public class SomeBean {
@PersistenceContext
private EntityManager em;

@Transactional
public void doSomething() {
// use em to access the database
}
}
The merge method can be used to store (insert or update) objects in the database:
Person person = new Person();
person.setName("Klaus Bergmann");
person.setAge(37);

// changes to the object returned by merge() are tracked by JPA and will be persisted when the transaction is committed
Person managedPerson = em.merge(person);
Person person = new Person();
person.setName("Klaus Bergmann");
person.setAge(37);

// changes to the object returned by merge() are tracked by JPA and will be persisted when the transaction is committed
Person managedPerson = em.merge(person);
On the other hand, the find method can be used to retrieve objects.
// find a Person using its primary key
Person p = em.find(Person.class, 123);
// find a Person using its primary key
Person p = em.find(Person.class, 123);
Of course, JPA is not just able to access objects using their primary key. For example, the createQuery can be used for more complex operations. This uses the Jakarta Persistence query language (https://jakarta.ee/learn/docs/jakartaee-tutorial/current/persist/persistence-querylanguage/persistence-querylanguage.html) which uses object information (e.g. field names) instead of database table/column names.
Query query = em.createQuery("SELECT p FROM Person p WHERE p.age = :personAge");
query.setParameter("personAge", 37);
List<Person> people = (List<Person>) query.getResultList();
Query query = em.createQuery("SELECT p FROM Person p WHERE p.age = :personAge");
query.setParameter("personAge", 37);
List<Person> people = (List<Person>) query.getResultList();
JavaObi
JavaObi3w ago
JPA is able to represent 1:1, 1:n, and n:m relations. For instance, an 1:n relation could be represented like this:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private int age;

@ManyToOne
@JoinColumn(name = "address_id")
private Address address;

// getters/setters omitted in this example
}
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private int age;

@ManyToOne
@JoinColumn(name = "address_id")
private Address address;

// getters/setters omitted in this example
}
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long addressId;

private String country;
private int postalCode;
private String cityName;
private String street;
private String houseNumber;

// optionally, the relation can be added in the other direction as well
@OneToMany(mappedBy = "address")
private List<Person> peopleAtAddress = new ArrayList<>();

// getters/setters omitted in this example
}
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long addressId;

private String country;
private int postalCode;
private String cityName;
private String street;
private String houseNumber;

// optionally, the relation can be added in the other direction as well
@OneToMany(mappedBy = "address")
private List<Person> peopleAtAddress = new ArrayList<>();

// getters/setters omitted in this example
}
📖 Sample answer from dan1st
JavaObi
JavaObi3w ago
JPA (Jakarta Persistence) is a specification in Java that defines how to manage relational data in Java applications. It provides a standard way to map Java objects (entities) to database tables and handle operations like storing, updating, deleting, and retrieving data. It is not itself a framework but a set of guidelines. Frameworks like Hibernate, EclipseLink, or OpenJPA implement JPA.
Submission from im.adharv_singh
JavaObi
JavaObi3w ago
JPA is sort of the bridge between the java application and the database that is used with your application, you can write in java to your database and Jakarta will automatically translate it to your database language like sql and also back. It has many other features to make the life of a dev easier in terms of database with your java app
Submission from bentekeb
JavaObi
JavaObi3w ago
it's a high level api to access your database. it doesnt depend on a database that makes it a good solution (yes, it's possible to use native queries and use syntax and some function of the database). You have to have an entity
@Data @Builder @NoArgsConstructor @AllArgsConstructor @Entity(name = "STORE") public class StoreEntity { @Id private String id; private String name; @Column(name = ""year"") private int year; private String country; }
And it's jpa repository class
public interface StoreRepository extends JpaRepository<StoreEntity, String> {}
Here is an example of my configuration of db2 database spring.datasource.url=jdbc:h2:mem:command;DB_CLOSE_DELAY=-1;AUTO_RECONNECT=TRUE
spring.datasource.username=root spring.datasource.password=root spring.datasource.driverClassName=org.h2.Driver spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=update
Submission from acidserg
JavaObi
JavaObi3w ago
JPA is a standard, under the JakartaEE umbrella, for defining an application's data access layer. It provides mechanisms for mapping between application objects and relational databases. As a standard, it can be implemented by any number of libraries, which can then be swapped out in the application with (theoretically) no loss of functionality. The most common implementations are: * Hibernate * EclipseLink The goal of JPA is to allow "plain old Java objects" (POJOs) to be used when representing persistent data within the application. These objects do not usually need to define special persistence-related behavior. Instead, they are constructed, modified, and read just as any Java object would be. JPA takes care of the details of reading the objects from and writing them to the database. Annotations are used on the POJOs to declare how the properties of the object map to the database. A few of the more important annotations are: * @jakarta.persistence.Entity - Used on a POJO class to mark it as "persistent" * @jakarta.persistence.Id - Defines a property as being the primary key of the data * @jakarta.persistence.Transient - Marks a POJO property as not being persistent. All Entity properties are persistent by default. Entity classes have other requirements and restrictions, such as needing a public or protected no-argument constructor. See the full specification for details. Another important concept is the entity manager (jakarta.persistence.EntityManager). Entity managers are the entry point to a particular data store. They maintain the connection along with the transaction context of a set of operations. They are created by the jakarta.persistence.EntityManagerFactory. In turn, EntityManagerFactory objects are created and registered using the jakarta.persistence.Persistence class, which is a top-level entry point into the JPA framework, used by plain Java SE applications or other environments that do not have dependency injection available.
JavaObi
JavaObi3w ago
That said, the most common usage of JPA occurs in applications using JakartaEE, Spring, or other runtimes that make use of dependency injection. In such environments, components can declare properties with the @jakarta.persistence.PersistenceUnit or @jakarta.persistence.PersistenceContext annotations to inject the EntityManagerFactory or current EntityManager, respectively. The EntityManager is then the gateway for the application to interact with the data store, e.g. managing transactions, performing queries, and saving objects. More feature-rich frameworks like Spring define higher-level constructs and mechanisms to improve the developer experience with JPA.
⭐ Submission from dangerously_casual

Did you find this page helpful?