Introduction to Hibernate and JPA

What is data persistence?

Data Persistence is a way for an application to store the data to a non volatile storage so that it can survive even after the process with which it was created has ended. Consider non volatile memory as a storage that has the capability to hold saved data even if the power is turned off.

For example, you have written a code to write an image data to a file for future use. Even if the code which wrote that file has stopped, the files that were created will still exist. This is persistence. In the future, you can open that file or even edit or delete that file.

Similarly, in a Java based application, you can store the data in a relational database using SQL and in future you can select, update or delete that data.

Why is it important?

Almost all applications deal with data. An application can create, view or manipulate data. If an application just creates or manipulates data but doesn’t preserve that data, when the application is powered off, all the data will be lost. For example, let’s assume you are working on a Word document, but you haven’t clicked the save button. If you close that document or shutdown your system, all the updates you have made will be lost. The update will only be persisted if you click the save button. After clicking on save, if you restart your system, you will still be able to see the updates. So, unless you persist the data, an application would be of little practical use.

Java Database Connectivity (JDBC) API

When you communicate with an SQL database from a Java application, you issue SQL statements to the database using JDBC API. There are few steps that you follow while using JDBC API –

  1. Initially, you open a database connection with the help of a JDBC driver. 
  2. After the connection is established, you send SQL queries using a statement.
  3. If you issue a select query, you get the result as ResultSet.
  4. Once you get the result, normally you copy values from the result set to the corresponding Java objects. 
  5. Finally, we need to close the connection to the database.

As you can see, interaction with databases using JDBC API is often tedious. You have to write a lot of code by yourself and you have to have a fair knowledge of SQL. But most of the time, you may be interested in the business logic rather than this low level hard work. Now the question is, is there an alternate and easy solution available to get rid of this hard work?

Object-Relational Mapping (ORM)

ORM is a technique that lets you select and manipulate data from a database. It sits between the SQL database and your object-oriented programming and encapsulates the code needed to communicate with the database. So instead of directly using SQL, you can interact with an object to get the job done.

So, there are few straight-forward benefits –

  1. As ORM eliminates a lot of tedious work to communicate with databases, it reduces development time.
  2. As you have to write less code which is easy to understand, write and update, it increases maintainability.
  3. ORM can provide a certain level of caching. So, it can improve the performance.
  4. ORM can help you to port to different databases.
JPA and Hibernate

Java Persistence API (JPA) is a Java specification or standard for ORM tools. Consider JPA as a set of rules and guidelines to implement object-relational mapping. JPA classes and interfaces are present under the javax.persistence package.

JPA is just a specification, it is not an implementation. Different ORM tools actually implemented those specifications. Hibernate is just one of them.

When we use Hibernate, we specify mapping between database tables and java objects. To specify mapping, we use JPA provided annotations. Once the mapping is in place, there is no need to write JDBC code to define how Java objects will be saved and retrieved from the database. It will be taken care of by Hibernate.

Persistence unit

A JPA Persistence Unit is a logical grouping of persistable classes under a database connection. So, every application has at least one persistence unit. If an application is interacting with multiple databases, there may be multiple persistence units with unique names. Each persistence unit is defined in a persistence-unit element in the persistence.xml file. We put this persistence.xml file under the META-INF directory.

<!-- Path: src/main/resources/META-INF/persistence.xml -->
<persistence>
    <persistence-unit name="some-name">
        <class>com.demo.PersistableClassName1</class>
        <class>com.demo.PersistableClassName2</class>
        <properties>
            <!-- DB connection details -->
        </properties>
    </persistence-unit>
</persistence>
Hibernate Maven dependency

Throughout the Hibernate tutorial we will use Apache Maven as the dependency management tool. To declare dependency on Hibernate, we can add the following dependency in the pom.xml file.

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.6.9.Final</version>
</dependency>
Persistent class

Java classes whose instances can be stored in database tables are called persistent classes or Entity classes in Hibernate.

Let’s consider a simple User class with the following attributes.

If you want to persist user objects to the database, you will have a corresponding user table in the database. Also, for each attribute of the User class, you will have corresponding columns in that table. Each insert statement will create a new row in the table.

You have to use certain annotations to map the persistent class with the table.

Every persistent class must have @Entity annotation. By default, the class name will be used as table name. If you want to use a different table name than the class name, you have to use @Table annotation and provide the name. You can also provide a schema name under @Table annotation. Both table name and schema names are case insensitive.

By default hibernate will consider every attribute as a persistent property for an entity class (persistent class). But you might not want to persist all properties in the database. To exclude a property, mark that field with @Transient annotation or use Java transient keyword for that field.

Also, by default, the field name will be used as column name. If you want to use a different column name, you have to use @Column annotation. This column name is case insensitive. Under this @Column annotation, you can optionally specify if the column is nullable or not.

Every persistent class must have an identifier attribute which corresponds to the primary key column of the table. This attribute must be annotated with @Id.

If you want to automatically generate the primary key value, you have to use @GeneratedValue annotation and provide a strategy to generate the primary key. If you do not use this annotation, you have to assign the identifier value manually before you save an entity.

Also remember, all persistent classes should have a no argument constructor. If you don’t write a constructor that is also fine as Java compiler will add a default constructor for you.

So, our User class will look something like –

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "USER", schema = "demo")
public class User {
    
    @Id
    @Column(name = "USER_ID")
    private long userId;
    
    @Column(name = "USER_NAME")
    private String userName;
    
    @Column(name = "EMAIL")
    private String email;
    
    @Column(name = "CITY")
    private String city;
    
    @Column(name = "STATE")
    private String state;
    
    @Column(name = "COUNTRY")
    private String country;
    
    // getters and setters
}
Entity object and value object

There are two types of object in hibernate, entity object and value object. Entity objects are those who can stand alone and have tables corresponding to it. Value object cannot stand alone and doesn’t have a table corresponding to it. Normally we use value object as an attribute of entity object.

In our User class, we have multiple attributes related to address. We may want to create a separate class for Address and use that as an attribute of the User class. But in most of the cases, we still want a single table to contain both User and address columns.

So we can say, User is the entity object and Address is the value object. When we create a value object, we mark that with @Embeddable annotation and when we use this value object in entity class as an attribute, we annotate that attribute with @Embedded.

@Entity
@Table(name = "USER", schema = "demo")
public class User {
    
    @Id
    @Column(name = "USER_ID")
    private long userId;
    
    @Column(name = "USER_NAME")
    private String userName;
    
    @Column(name = "EMAIL")
    private String email;
    
    @Embedded
    private Address address;
    
    // getters and setters
}
@Embeddable
public class Address {
    
    @Column(name = "CITY")
    private String city;
    
    @Column(name = "STATE")
    private String state;
    
    @Column(name = "COUNTRY")
    private String country;
    
    // getters and setters
}

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.