Java/Jakarta Persistence API(JPA)
- Jakarta Persistence API is formerly known as Java Persistence API.
- JPA is a specification that facilitates object-relational mapping to manage relational data in Java applications.
- Most of the ORM tool like Hibernate, IBatis, Toplink are implemented JPA standards.
- It provides a support to work directly with object instead of using SQL statements.
- If you need to switch from IBatis to Hibernate in your Java application it will be easier because you are just changing the tool. But specification is still same. That is the advantage of using JPA.
- You can not directly use JPA. If you are going with JPA, you should use Hibernate, IBatis ORM tools which using JPA specification.
JPA Versions
- JPA 2.0
- JPA 2.1
- JPA 2.2
- JPA 3.0 — The JPA was renamed as Jakarta Persistence in 2019 and version 3.0 was released in 2020. All the packages are renames from javax.persistence to jakarta.persistence.
- DataNucleus (from version 6.0)
- EclipseLink (from version 3.0)
- Hibernate (from version 5.5)
5. JPA 3.1 — released in 2022. Requires at least Java 11 to run.
- DataNucleus (from version 6.0)
- EclipseLink (from version 4.0)
- Hibernate (from version 6.0)
JPA Entity
JPA Entity is POJO class with JPA annotations. It represents a table stored in a database.
Example 1 —
Here we are using GenerationType Identity to generate primary key value.
import jakarta.persistence.*;
@Entity
public class Fruit {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "name")
private String name;
public Fruit(int id, String name) {
this.id = id;
this.name = name;
}
public Fruit(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fruit{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Example 2 -
Here we are using SequenceGenerator to generate the primary key. inital value is 100. it means primary key auto increment is starting from 100.
package org.example.model;
import jakarta.persistence.*;
@Entity
public class Vegetable {
@Id
@SequenceGenerator(
name = "vegetable_id_seq",
sequenceName = "vegetable_id_seq",
allocationSize = 1,
initialValue = 100
)
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "vegetable_id_seq"
)
private int id;
@Column(
nullable = false
)
private String name;
@Column(
nullable = false
)
private double price;
public Vegetable(String name, double price) {
this.name = name;
this.price = price;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
Configure Postgres EntityManager
Here I am using postgres database.
version: "3"
services:
postgres:
image: postgres:9.5
network_mode: bridge
container_name: postgres
expose:
- 5432
ports:
- 5432:5432
environment:
- POSTGRES_PASSWORD=docker
- POSTGRES_USER=docker
- POSTGRES_DB=docker
restart: unless-stopped
These are the dependencies,
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core-jakarta</artifactId>
<version>5.6.14.Final</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1206-jdbc42</version>
</dependency>
We should create persistence.xml file to configure all the postgres drivers, username, password and etc.
According to the above image, we should add persistence.xml file into recources/META-INF.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="default">
<class>org.example.model.Fruit</class>
<class>org.example.model.Vegetable</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://MACHINE_IP>:5432/docker"/>
<property name="jakarta.persistence.jdbc.user" value="docker"/>
<property name="jakarta.persistence.jdbc.password" value="docker"/>
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
</properties>
</persistence-unit>
</persistence>
These are few configuration parameters.
JPA Entity Manager
- Entity manager is used to read, delete and write an entity.
- Here I am using default persistence-unit which we defined in persistence.xml file.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("default");
EntityManager em = emf.createEntityManager();
When we are using any entity manager functions, we should create a transaction using getTransaction().begin() method. We should do all the insert, update and delete within entity manager transaction.
1. Insert
//Insert
em.getTransaction().begin();
em.persist(fruit);
em.persist(vegetable);
em.persist(vegetable1);
em.getTransaction().commit();
2. Update
//Update
em.getTransaction().begin();
Vegetable selectedVeg = em.find(Vegetable.class, 101);
selectedVeg.setName("Potatoes");
em.persist(selectedVeg);
em.getTransaction().commit();
3. Delete
em.getTransaction().begin();
em.remove(em.find(Vegetable.class, 100));
em.getTransaction().commit();
4. Find
//Find
Fruit selectedFruit = em.find(Fruit.class, 1);
System.out.println(selectedFruit);
This is the complete program for App class.
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import org.example.model.Fruit;
import org.example.model.Vegetable;
public class App
{
public static void main( String[] args ) {
EntityManagerFactory emf = null;
EntityManager em = null;
try {
emf = Persistence.createEntityManagerFactory("default");
em = emf.createEntityManager();
Fruit fruit = new Fruit("Orange");
Vegetable vegetable = new Vegetable("Carrot", 23.2);
Vegetable vegetable1 = new Vegetable("Beet Root", 23.2);
//Insert
em.getTransaction().begin();
em.persist(fruit);
em.persist(vegetable);
em.persist(vegetable1);
em.getTransaction().commit();
//Delete
em.getTransaction().begin();
em.remove(em.find(Vegetable.class, 100));
em.getTransaction().commit();
//Update
em.getTransaction().begin();
Vegetable selectedVeg = em.find(Vegetable.class, 101);
selectedVeg.setName("Potatoes");
em.persist(selectedVeg);
em.getTransaction().commit();
//Find
Fruit selectedFruit = em.find(Fruit.class, 1);
System.out.println(selectedFruit);
} catch (Exception ex) {
System.out.println(ex.getMessage());
} finally {
if (emf != null) {
emf.close();
}
if (em != null) {
em.close();
}
}
}
}
Here we are using <property name=”jakarta.persistence.schema-generation.database.action” value=”drop-and-create”/>. Every time when we run the application, it is drop the existing entities and create new entities.