DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Simplify Java Persistence Using Quarkus and Hibernate Reactive
  • Realistic Test Data Generation for Java Apps
  • How Spring and Hibernate Simplify Web and Database Management
  • Spring Microservice Tip: Abstracting the Database Hostname With Environment Variable

Trending

  • AI-Based Threat Detection in Cloud Security
  • How to Practice TDD With Kotlin
  • Docker Base Images Demystified: A Practical Guide
  • Docker Model Runner: Streamlining AI Deployment for Developers
  1. DZone
  2. Data Engineering
  3. Databases
  4. Getting Started With JPA/Hibernate

Getting Started With JPA/Hibernate

This article will get you started with the Hibernate implementation of JPA. We will use only standard features and cover the basics to provide an overview.

By 
Alejandro Duarte user avatar
Alejandro Duarte
DZone Core CORE ·
Jan. 29, 22 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
48.3K Views

Join the DZone community and get the full member experience.

Join For Free

JPA was born as the 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 solves the object-relational impedance mismatch by allowing you to map Java objects to database tables and is one of the most (if not the most) used persistence frameworks for Java.

JPA is an API specification that someone can implement–JPA providers or implementations. The most popular JPA implementation is Hibernate ORM. You can use Hibernate ORM without JPA or through JPA. One potential advantage of using it with JPA is that you can move between implementations if you want (something I have never seen happening, though). Another advantage is that someone with experience in, say EclipseLink or Apache OpenJPA, then they can use at least part of that experience when moving to Hibernate.

This article gets you started with the Hibernate implementation of JPA. We’ll use only standard features and cover only the basics. If you are looking for a comprehensive course on JPA or Hibernate, I recommend reading the JPA spec and the official Hibernate docs.

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


Adding JPA and Hibernate to a Maven Project

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

XML
 
<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 a 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 the 3.0 series):

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


If you use Gradle, you can use an online Maven to Gradle dependency converter.

Creating a Database

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

Note: You can create a MariaDB database in the cloud using SkySql. It's free and it doesn’t require credit card details. Create an account and take a look at the documentation for more information on how to get started.

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

MariaDB SQL
 
CREATE DATABASE jpa_demo;


Defining a Persistence Unit

A Persistence Unit is the logical grouping of a set of classes of which objects can be persisted, plus configuration parameters like database connection and pooling options. A Persistence Unit is defined in a persistence.xml file in the src/main/resources/META-INF/ directory. Here’s the one we’ll use in this article:

XML
 
<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 the name and transaction-type parameters. Every Persistence Unit needs a name that we can use later in the code. The transaction type indicates who manages the transactions, JPA or the server. In this example we want JPA to manage the transactions, but in more complex scenarios that require distributed transactions across multiple databases or services like JMS and JCA, we would use JTA instead. This is an advanced topic we won’t cover in this article.

We added properties to specify the JDBC connection URL and the database user and password. We also activated a database action to drop and create the database schema automatically. This will make JPA drop the database (deleting all the tables and schema) and recreate them any time we start the application. This obviously is not a good idea in production environments, but very useful during development, especially when you are starting to define the database objects through JPA Entities.

Implementing an Entity

An Entity is a class of which instances we want to persist in the database. You can configure them through Java annotations or XML. Study the following example paying close attention to the annotations:

Java
 
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, a JPA Entity class is marked by @Entity, has a field annotated with @Id, a default constructor, and field accessors (getters). In the previous example, we added extra configuration. For example, we configured the names of the SQL table and column names, and a generated value to let the database assign primary keys. We also added a custom constructor (additional to the required non-arg constructor), equals(Object) and hashCode() methods, and field mutators (setters).

The entry point of the JPA API is the EntityManagerFactory class. Frameworks like Spring Framework and JakartaEE might provide instances of this type to your application. If you are not using them, you can create an instance specifying the name Persistence Unit to use:

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


Ideally, you should have only one instance of EntityManagerFactory for each Persistence Unit per application and create EntityManager objects (much cheaper to create) as needed. Persistence operations must be inside a transaction boundary:

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


The EntityManager class includes methods to query and update data. For example, if we want to save a ProgrammingLanguage object in the database, we can add the following persistence logic to the previous snippet of code:

Java
 
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 objects:

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


Notice that the query in the string is not SQL. If you recall from where we implemented the Entity (ProgrammingLanguage) we used the @Table annotation to configure the name of the table as programming_language. The query language is called Jakarta Persistence Query Language (JPQL), a platform-independent object-oriented language that is part of the JPA specification. This makes your database queries portable between databases.

Check some of the methods available in the EntityManager interface to get an idea of all the persistence operations available. This is the interface that you’ll use the most when using JPA directly.

Implementing a Simple JPA Service Class

Even though we used the most simple use case and ignored things such as transaction rollback in case of errors or controlled conditions, we need 5 lines of boilerplate code to run a simple query or to save, delete or update an Entity. In a Java SE application like the one we are implementing in this article, it’d be useful to have a utility class to encapsulate all the boilerplate code and make the usage of JPA easier. This utility class should include the logic to create one PersistenceManagerFactory per application, create a new EntityManager and begin, close, and commit transactions automatically.

Here’s an example of such a utility class implemented as a singleton that takes care of rolling back a transaction in case of a failure. Take into account that if you are using frameworks like Jakarta EE or Spring Data, you don’t really need this kind of class:

Java
 
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();
            }
        }
    }
}


By using this class we can simplify the code required to use an EntityManager. For example, to run a JQL query we only need to enclose the logic in a  runInTransaction(Function<EntityManager>, T) call:

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


Here’s an example of a full Java application that uses this class to create programming languages with random ratings and show the ones with a rating greater than 5:

Java
 
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 merely scratched the surface of JPA and Hibernate in this article. Fortunately, being one of the most popular Java specifications, a ton of useful resources are available online. Here’s a list of some of them:

  • The Jakarta Persistence 3.0 official page
  • The spec itself
  • The Javadoc
  • An intro to JDBC
  • The MariaDB JDBC driver documentation
  • This article as a video
  • JPA with Spring boot guide
  • JPA with Jakarta EE guide
Database connection Java (programming language) Persistence (computer science) Spring Framework application Hibernate

Opinions expressed by DZone contributors are their own.

Related

  • Simplify Java Persistence Using Quarkus and Hibernate Reactive
  • Realistic Test Data Generation for Java Apps
  • How Spring and Hibernate Simplify Web and Database Management
  • Spring Microservice Tip: Abstracting the Database Hostname With Environment Variable

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!