- Why Do We Need Automated Persistence? – The Case for Hibernate
- Developing a Basic Java Application – Getting Started Using Hibernate
- Defining a Persistable Entity – Hibernate’s Core Feature
- Accessing the Database – Creating a Persistence Unit
- Performing CRUD Operations on Entities – Working with Hibernate’s Entity Manager
- Adding a Nice API – Defining a DAO Class
- Final Thoughts
- Frequently Asked Questions (FAQs) about Hibernate in Java
When it comes to object persistence, the JPA/Hibernate tandem is one of those gems you’ll never want to leave behind once you get used to it. Thorben Janssen has written a great introduction to the subject, listing five good reasons why to use it. (It’s not a prerequisite for this article but I nevertheless strongly encourage you to give it a read.) In this tutorial we will focus on Hibernate’s core feature: persisting object graphs.
Why Do We Need Automated Persistence? – The Case for Hibernate
Let’s first answer the question why we would even use a framework to persist application data. Is JDBC (Java Database Connectivity) not good enough for us?
Well, if we look at the data held by our applications, we’ll usually see a complex network of objects, also known as the object graph, that we want to write to and read from some store, usually a relational database. Unfortunately, the persistence process is far from straightforward. Translating entire objects graphs to plain tabular data is everything but simple and storing objects using plain JDBC is painful and cumbersome, as the standard exposes a low-level API. You know, connecting to a database and writing SQL queries from scratch is all well and fine. But be honest with yourself: Do you want to do that over and over again? If you’re anything like me, you don’t.
And this is where Hibernate comes in. Besides a lot of other things, it allows us to create the objects that hold our data, called entities, as we are used to and automates the process of reading and writing them.
Let’s get started!
Developing a Basic Java Application – Getting Started Using Hibernate
A nice way to start pulling the reins of Hibernate is to create a simple example. To do so, I’m very going to develop a naive Java application, whose functionality will boil down to performing CRUD operations on some user objects. In turn, the objects will be persisted, fetched, updated and removed from a MySQL database.
To get things rolling as expected, we first need to grab the required Hibernate ORM JARs, along with the corresponding MySQL Java Connector. Maven will do that nicely for us, so we can focus on coding, rather than on dealing with dependency issues.
Here’s my POM file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.electricalweb.hibernatepplication</groupId>
<artifactId>hibernateapplication</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.2.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.3</version>
</dependency>
</dependencies>
</project>
In this particular case, I’ll be using IntelliJ IDEA as my personal IDE, but the project can be easily created in NetBeans, Eclipse, or the IDE of your choice.
With the POM file under the way, here’s how this sample application will be laid out:
As shown above, the application’s skeleton is structured in only three packages: com.electricalweb.entities
, which contains the domain class that models users, com.electricalweb.daos
, which houses a basic user DAO class, and lastly com.electricalweb.application
, which is the application’s entry point. Since the primary goal is to persist user objects in MySQL, let’s define the User
domain class.
Defining a Persistable Entity – Hibernate’s Core Feature
To keep things simple, the domain class will be extremely anemic, having only a couple of private properties, name
and email
, along with a parameterized constructor and the typical setters/getters. Here’s the class in question:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String email;
public User() {}
public User(String name, String email) {
this.setName(name);
this.setEmail(email);
}
// getters and setters for name and email
}
Yes, you’re right. The above class is just a lightweight data container with no behavior attached to it (feel free to judge me for committing this cardinal sin). The only detail worth to stress here is that in order to make it persistable for Hibernate (or any other JPA implementation) it’s been marked with an @Entity
annotation. In addition, Hibernate needs to know how to handle the entities’ primary keys.
To tackle this, the @Id
and @GeneratedValue(strategy = GenerationType.AUTO)
annotations instruct Hibernate to automatically generate an ID for each user entity, which will be mapped to the primary key of the corresponding database entry. Finally, the @Table(name = "users")
annotation tells Hibernate to map instances of the class to rows in a users
table.
With the domain class already defined, the final step to take in order to get the sample application up and running is create a configuration file, not surprisingly called persistence.xml
, which will contain all the data required for connecting to the database and mapping entities to the database.
Accessing the Database – Creating a Persistence Unit
Unquestionably, the most common way of setting up the connection parameters to the database, along with additional Hibernate proprietary options, is by defining the aforementioned persistence.xml
file. The process is pretty boring but extremely straightforward, trust me.
Here’s how a typical version of this file might look:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="user-unit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.electricalweb.entities.User</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/usersdb" />
<property name="javax.persistence.jdbc.user" value="username" />
<property name="javax.persistence.jdbc.password" value="password" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
As you can see, the file is easily readable from top to bottom. It just sets up a few database connection parameters, which you should be familiar with if you’ve worked with JDBC before, including the database URL, the JDBC driver, the database username and the password (make sure to provide the right values for your own database server).
Additionally, and this is by far the most interesting segment of the file, it defines a persistence-unit, that is a set of persistence entities (in this case just users, but it could be multiple entities wrapped inside several <class>
tags) which are handled by a JPA entity manager. Considering that in this case Hibernate is the chosen JPA implementation, the entities will be managed by Hibernate’s entity manager.
If you’re like me, at this point you’ll be eager to see how to use the entity manager for performing CRUD operations on a few user entities in a snap. Just relax. The next section covers the process in depth.
Performing CRUD Operations on Entities – Working with Hibernate’s Entity Manager
In a nutshell, running CRUD operations on user objects is much easier than one might think. The entire process just boils down to spawning the EntityManager
and consuming its API. It’s really that easy.
For instance, we could use the application’s entry point, which is a simple class with a plain static main
method, and call the entity manager in the following way:
EntityManager entityManager = Persistence
.createEntityManagerFactory("user-unit")
.createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
/* Persist a User entity */
entityTransaction.begin();
User user = new User("Alejandro", "alejandro@domain.com");
entityManager.persist(user);
entityTransaction.commit();
/* Fetch a User entity */
entityManager.find(User.class, 1);
/* Update a User entity */
entityTransaction.begin();
User user = entityManager.find(User.class, 1);
user.setName("Alex");
user.setEmail("alex@domain.com");
entityManager.merge(user);
entityTransaction.commit();
/* Remove a User entity */
entityTransaction.begin();
User user = entityManager.find(User.class, 1);
entityManager.remove(user);
entityTransaction.commit();
entityManager.close();
Quite simple, isn’t it? Saving, fetching, updating and deleting user entities with a non-managed entity manager is a no-brainer process that can be mastered literally within minutes. The procedure is limited to first getting the manager via an EntityManagerFactory
, then grabbing an instance of the EntityTransaction
class, and finally calling the desired method of the entity manager.
The above code snippet assumes there’s a MySQL database table called users
already defined, together with a MySQL instance running on a local server. So, make sure to set up that before testing the sample application.
At this point, you hopefully learned the basics behind persisting single entities with Hibernate. So, what comes up next? Well, a lot actually. As you may have noticed, I left out of the picture persisting entities that have a specific relationship with other entities (yes, the relational aspect). I did this deliberately, in order to keep the whole learning process free from additional, unnecessary complexities. After all, this is just an introduction to getting started using Hibernate.
Still, I’d like to polish the demo a little bit, as in its current state it looks pretty monolithic and its execution flow can’t be controlled through a user interface.
Taking this into consideration, what I’ll do next will be defining a DAO class, which will encapsulate the handling of the entity manager and the entity transaction behind the fences of a declarative API. Finally, you’ll see how to use the class for selectively executing CRUD methods on user entities, based on options entered in the Java console.
Adding a Nice API – Defining a DAO Class
Believe me. I’m not a blind worshiper of design patterns, using them everywhere without having strong reasons to do so. In this case, however, it’s worth to appeal to the functionality provided by the DAO pattern, so you can give the previous example a try without much hassle.
In a nutshell, all that the following UserDao
class does is to define an easily consumable API, which allows to execute CRUD methods on a given user entity. Here’s how the class looks:
public class UserDao {
private EntityManager entityManager;
private EntityTransaction entityTransaction;
public UserDao(EntityManager entityManager) {
this.entityManager = entityManager;
this.entityTransaction = this.entityManager.getTransaction();
}
public void persist(String name, String email) {
beginTransaction();
User user = new User(name, email);
entityManager.persist(user);
commitTransaction();
}
public User find(int id) {
return entityManager.find(User.class, id);
}
public void update(int id, String name, String email) {
beginTransaction();
User user = entityManager.find(User.class, id);
user.setName(name);
user.setEmail(email);
entityManager.merge(user);
commitTransaction();
}
public void remove(int id) {
beginTransaction();
User user = entityManager.find(User.class, id);
entityManager.remove(user);
commitTransaction();
}
private void beginTransaction() {
try {
entityTransaction.begin();
} catch (IllegalStateException e) {
rollbackTransaction();
}
}
private void commitTransaction() {
try {
entityTransaction.commit();
entityManager.close();
} catch (IllegalStateException e) {
rollbackTransaction();
}
}
private void rollbackTransaction() {
try {
entityTransaction.rollback();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
Honestly, this additional layer of abstraction (and complexity) wouldn’t make much sense per see if I wouldn’t show you how to use the class in a concrete case. So, check the following snippet, which exploits the benefits of dependency injection.
public class HibernateApplication {
private final UserDao userDao;
private final BufferedReader userInputReader;
public static void main(String[] args) throws Exception {
EntityManager entityManager = Persistence
.createEntityManagerFactory("user-unit")
.createEntityManager();
UserDao userDao = new UserDao(entityManager);
BufferedReader userInputReader =
new BufferedReader(new InputStreamReader(System.in));
new HibernateApplication(userDao, userInputReader).run();
}
public HibernateApplication(UserDao userDao, BufferedReader userInputReader) {
this.userDao = userDao;
this.userInputReader = userInputReader;
}
private void run() throws IOException {
System.out.println("Enter an option: "
+ "1) Insert a new user. "
+ "2) Find a user. "
+ "3) Edit a user. "
+ "4) Delete a user.");
int option = Integer.parseInt(userInputReader.readLine());
switch (option) {
case 1:
persistNewUser();
break;
case 2:
fetchExistingUser();
break;
case 3:
updateExistingUser();
break;
case 4:
removeExistingUser();
break;
}
}
private void persistNewUser() throws IOException {
String name = requestStringInput("the name of the user");
String email = requestStringInput("the email of the user");
userDao.persist(name, email);
}
private void fetchExistingUser() throws IOException {
int id = requestIntegerInput("the user ID");
User user = userDao.find(id);
System.out.print("Name : " + user.getName() + " Email : " + user.getEmail());
}
private void updateExistingUser() throws IOException {
int id = requestIntegerInput("the user ID");
String name = requestStringInput("the name of the user");
String email = requestStringInput("the email of the user");
userDao.update(id, name, email);
}
private void removeExistingUser() throws IOException {
int id = requestIntegerInput("the user ID");
userDao.remove(id);
}
private String requestStringInput(String request) throws IOException {
System.out.printf("Enter %s: ", request);
return userInputReader.readLine();
}
private int requestIntegerInput(String request) throws IOException {
System.out.printf("Enter %s: ", request);
return Integer.parseInt(userInputReader.readLine());
}
}
As shown above, the HibernateAplication
class accepts a range of four different values entered in the console. According to the given input, it uses the DAO for running a specific CRUD operation on a user entity. Needless to say the logic of this revamped example is self-explanatory, so any further analysis would be redundant. Thus, make sure to give it a try on your own development platform and feel free to twist it at will to suit your personal needs.
Final Thoughts
In this tutorial, I walked you through a concise guide on using Hibernate for persisting entities in a database. Naturally, the framework is a powerful beast that offers a lot more than just plain ORM functionality, including full-text search capabilities, Geolocation, Domain Models validation and much more. For obvious reasons, covering all these features in just one article would be not only impossible, but impractical. Of course, if you want to test the sample application and rework it at will, feel free to clone the repository from GitLab (https://gitlab.com/alejandrogervasio/hibernate.git)
Anyway, if you’re a newcomer to Hibernate and want to start sinking your teeth in it right away, this guide hopefully will be a good starting point. Feel free to drop me your comments. I’ll try to reply in a timely fashion. See you in the next article!
Frequently Asked Questions (FAQs) about Hibernate in Java
What are the key differences between Hibernate and JPA?
Hibernate is an Object-Relational Mapping (ORM) framework that provides a reference implementation for the Java Persistence API (JPA), which makes it a superset of JPA. Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities. On the other hand, JPA is a specification for ORM in Java. It provides a set of concepts and APIs that a developer can use to integrate Java applications with relational databases.
How does Hibernate improve performance?
Hibernate improves performance by using various optimization techniques such as lazy loading, fetching strategies, and caching. Lazy loading delays the loading of an object until it is needed, while fetching strategies determine how Hibernate retrieves associated objects if the application needs to navigate the association. Hibernate also supports two levels of cache: first-level cache and second-level cache. The first-level cache is associated with the Session object, while the second-level cache is associated with the Session Factory object.
What is the Hibernate Query Language (HQL)?
HQL is an object-oriented query language, similar to SQL, but it operates on persistent objects rather than on database tables. HQL queries are translated by Hibernate into conventional SQL queries, which in turns perform action on database. Although HQL is similar to SQL, it’s not exactly the same. The differences are mostly due to the fact that SQL is a data-centric language, while HQL is an object-centric language.
How does Hibernate handle transactions and concurrency?
Hibernate provides a powerful and flexible transaction model that is abstracted from the underlying JDBC or JTA transaction. A transaction is associated with a Session and is usually instantiated by a call to session.beginTransaction(). Hibernate also provides an optimistic locking mechanism to handle concurrency, which is a strategy where conflicts are detected at commit time.
What is the role of the SessionFactory in Hibernate?
The SessionFactory is a thread-safe (and immutable) representation of the mapping of the application domain model to a database. It’s usually created during application startup and kept for later use. It serves as a factory for Session instances, which are not thread-safe and should be created and destroyed as needed.
How does Hibernate handle inheritance mapping?
Hibernate supports three types of inheritance mapping: table per class hierarchy, table per subclass, and table per concrete class. Each of these strategies has its own advantages and disadvantages, and the choice of strategy depends on the specific requirements of your application.
What is the difference between get() and load() methods in Hibernate?
The get() method returns the persistent instance with the given identifier, or null if there is no such persistent instance. On the other hand, the load() method returns the persistent instance with the given identifier, assuming that the instance exists. If the instance does not exist, this method might return a proxy, or throw an exception when the instance is accessed.
How can I integrate Hibernate with Spring Framework?
Spring provides excellent integration with Hibernate through its ORM module. You can use Spring’s HibernateTemplate or HibernateDaoSupport classes, or you can use the Hibernate APIs directly. Spring takes care of the creation and release of resources, transaction management, exception handling, and other common tasks.
What is the Criteria API in Hibernate?
The Criteria API is a high-level API provided by Hibernate for fetching data from the database based on certain criteria. It’s more flexible and easier to use than HQL or SQL, especially for complex queries. The Criteria API allows you to build up a criteria query in a programmatic and secure manner, without the need to know the database schema or the names of the columns.
How can I handle database migrations with Hibernate?
Hibernate provides a tool called SchemaExport for exporting a database schema. It can either create the schema directly in the database, or generate SQL scripts that you can run manually. However, for more complex database migrations, you might want to use a dedicated tool like Flyway or Liquibase.
Alejandro Gervasio is a senior System Analyst from Argentina who has been involved in software development since the mid-80's. He has more than 12 years of experience in PHP development, 10 years in Java Programming, Object-Oriented Design, and most of the client-side technologies available out there.