Getting Started With JPA/Hibernate – DZone Java

JPA was born as an acronym for Java Persistence API. When Java EE was donated to the Eclipse Foundation under the Jakarta EE project, the name of the API changed to Jakarta Persistence but the term JPA is still in use. JPA resolves object-related impedance mismatch by allowing you to map Java objects to database tables and is one of the most (if not the most) used of Java’s persistence frameworks.

JPA is an API specification that can be implemented by anyone – JPA providers or implementations. The most popular JPA implementation is Hibernate ORM. You can use Hibernate ORM without JPA or with JPA. One potential advantage of using it with JPA is that you can cycle through implementations if you want (something I haven’t seen done before). Another advantage is that someone with experience with, such as EclipseLink or Apache OpenJPA, can then use at least part of that experience when switching to Hibernate.

This article helps you get started with the Hibernate JPA Application. We’ll only use standard features and cover only the basics. If you are looking for a comprehensive course on JPA or Hibernate, I recommend reading JPA Specifications and the official Hibernate documentation.

Here’s a video version of this article, if you want to see the concepts in action:

Adding JPA and Hibernate to Maven Project

Here are the Maven dependencies you need to add to a file pom.xml a file:

<dependency>
    <groupId>jakarta.persistence</groupId>
    <artifactId>jakarta.persistence-api</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core-jakarta</artifactId>
    <version>5.6.4.Final</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>3.0.0</version>
</dependency>

You also need the JDBC driver. For example, if you are using a MariaDB database or SkySQL instance, add the following as well (check the latest version and see what’s new in Series 3.0):

<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>3.0.3</version>
</dependency>

If you are using Gradle, you can use the online Maven to Gradle dependency converter.

Create a database

You can use any database you want, but let’s continue with MariaDB.

Noticeable: You can create a MariaDB database in the cloud with SkySql. It is free and does not require credit card details. Create an account and look at the documentation for more information on how to get started.

Connect to the database using SQL client and create the following database:

CREATE DATABASE jpa_demo;

Determine the stability unit

The persistence unit is the logical grouping of a set of classes in which objects can be persisted, as well as configuration parameters such as database connection and grouping options. The unit of stability is defined in perseverance. xml file in src / main / resources / META-INF / Guide. Here’s what we’ll be using in this article:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
    <persistence-unit name="jpa-demo-local" transaction-type="RESOURCE_LOCAL">
        <properties>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:mariadb://localhost:3306/jpa_demo"/>
            <property name="jakarta.persistence.jdbc.user" value="user"/>
            <property name="jakarta.persistence.jdbc.password" value="password"/>
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
        </properties>
    </persistence-unit>
</persistence>

Pay close attention to name And transaction-type the teachers. Each persistence module needs a name that we can use later in the code. Transaction type refers to the person managing the transactions, the JPA or the server. In this example, we want JPA to manage transactions, but in more complex scenarios that require transactions distributed across multiple databases or services like JMS and JCA, we’ll use JTA instead. This is an advanced topic that we will not cover in this article.

We have added properties to specify the JDBC link URL, database user, and password. We also activated a database action to automatically drop and create the database schema. This will cause JPA to drop the database (delete all tables and schema) and recreate it any time we start the application. This is obviously not a good idea in production environments, but it is very useful during development, especially when you start defining database objects through JPA entities.

Entity Execution

that a personality It is a class of instances that we want to persist in the database. You can configure it through Java or XML annotations. Study the following example and pay attention to the annotations:

package com.example;
 
import jakarta.persistence.*;
 
import java.util.Objects;
 
@Entity
@Table(name = "programming_language")
public class ProgrammingLanguage {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "pl_id")
    private Integer id;
 
    @Column(name = "pl_name")
    private String name;
 
    @Column(name = "pl_rating")
    private Integer rating;
 
    public ProgrammingLanguage() {
    }
 
    public ProgrammingLanguage(String name, Integer rating) {
        this.name = name;
        this.rating = rating;
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ProgrammingLanguage that = (ProgrammingLanguage) o;
        return Objects.equals(id, that.id);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Integer getRating() {
        return rating;
    }
 
    public void setRating(Integer rating) {
        this.rating = rating;
    }
}

At a minimum, the JPA entity class is marked by @Entitywith an annotated field @Id, default constructor, and field input (quotient). In the previous example, we added an additional configuration. For example, we configured SQL table names and column names, and a value created to allow the database to set primary keys. We also added a custom constructor (in addition to the required non-intermediate constructor), equals(Object) And hashCode() Methods, field mutants (authors).

JPA API entry point is a file EntityManagerFactory Class. Frameworks like Spring Framework and JakartaEE may provide instances of this type for your application. If you’re not using it, you can create an instance specifying the name of the persistence module to use:

EntityManagerFactory entityManagerFactory =
    Persistence.createEntityManagerFactory("jpa-demo-local");

Ideally, you should have only one instance of EntityManagerFactory Per persistence per application and creation EntityManager Objects (much cheaper to create) as needed. Persistences must be within transaction boundaries:

EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
 
... persistence logic here ...
 
transaction.commit(); // or transaction.rollback();

the EntityManager The class includes methods for querying and updating data. For example, if we want to save a file ProgrammingLanguage object in the database, we can add the following continuity logic to the previous code snippet:

entityManager.persist(new ProgrammingLanguage("Java", 10));

Or if we want to get all the programming languages ​​stored in the database as a list of ProgrammingLanguage Things:

List<ProgrammingLanguage> list = entityManager.createQuery(
        "select p from ProgrammingLanguage p where p.rating > 5",
        ProgrammingLanguage.class
).getResultList();

Note that the query in the string is not SQL. If you remember where we applied the entity (ProgrammingLanguage) we used the @Table Explanation of creating a table name as programming_language. The query language is called Jakarta Persistent Query Language (JPQL), and it is a platform-based, object-oriented language that forms part of the JPA specification. This makes your database queries transferable between databases.

Check out some of the methods available at EntityManager Interface to get an idea of ​​all available persistence operations. This is the interface that you will use the most when using JPA directly.

Simple JPA Service Class Implementation

Although we used the simplest use cases and ignored things like rolling back transactions in case of errors or controlled conditions, we need 5 lines of standard code to run a simple query, save, delete or update an entity. In a Java SE application like the one we’re implementing in this article, it would be helpful to have a Tools class to encapsulate all of your standard code and make JPA easier to use. The utility class must include logic to create one PersistenceManagerFactory For each application, create a file EntityManager and start, close and complete transactions automatically.

Here is an example of a utility class implemented as a single individual that will roll back a transaction in the event of a failure. Keep in mind that if you are using frameworks like Jakarta EE or Spring Data, you don’t really need this type of class:

package com.example;
 
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;
 
import java.util.function.Function;
 
public class JPAService {
 
    private static JPAService instance;
    private EntityManagerFactory entityManagerFactory;
 
    private JPAService() {
        entityManagerFactory = Persistence.createEntityManagerFactory("jpa-demo-local");
    }
 
    public static synchronized JPAService getInstance() {
        return instance == null ? instance = new JPAService() : instance;
    }
 
    public void shutdown() {
        if (entityManagerFactory != null) {
            entityManagerFactory.close();
            instance = null;
        }
    }
 
    public <T> T runInTransaction(Function<EntityManager, T> function) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        boolean success = false;
        try {
            T returnValue = function.apply(entityManager);
            success = true;
            return returnValue;
 
        } finally {
            if (success) {
                transaction.commit();
            } else {
                transaction.rollback();
            }
        }
    }
}

With this class we can simplify the code required to use a file EntityManager. For example, to run a JQL query, we only need to include the logic in a file runInTransaction(Function<EntityManager>, T) communicate:

List<ProgrammingLanguage> programmingLanguages = jpaService.runInTransaction(entityManager ->
        entityManager.createQuery(
                "select p from ProgrammingLanguage p where p.rating > 5",
                ProgrammingLanguage.class
        ).getResultList());

Here is an example of a complete Java application that uses this class to generate programming languages ​​with random ratings and show languages ​​with a higher rating out of 5:

package com.example;
 
import java.util.Arrays;
import java.util.List;
 
public class Application {
 
    private static final JPAService jpaService = JPAService.getInstance();
 
    public static void main(String[] args) {
        try {
            createProgrammingLanguages();
            printTopProgrammingLanguages();
 
        } finally {
            jpaService.shutdown();
        }
    }
 
    private static void createProgrammingLanguages() {
        jpaService.runInTransaction(entityManager -> {
            Arrays.stream("Java,C++,C#,JavaScript,Rust,Go,Python,PHP".split(","))
                    .map(name -> new ProgrammingLanguage(name, (int) (Math.random() * 10)))
                    .forEach(entityManager::persist);
            return null;
        });
    }
 
    private static void printTopProgrammingLanguages() {
        List<ProgrammingLanguage> programmingLanguages = jpaService.runInTransaction(entityManager ->
                entityManager.createQuery(
                        "select p from ProgrammingLanguage p where p.rating > 5",
                        ProgrammingLanguage.class
                ).getResultList());
 
        programmingLanguages.stream()
                .map(pl -> pl.getName() + ": " + pl.getRating())
                .forEach(System.out::println);
    }
}

Useful Resources

We’ve only scratched the surface of JPA and Hibernate in this article. Fortunately, for it being one of the most popular Java specifications, plenty of useful resources are available online. Here is a list of some of them:

.

Leave a Comment