In this article, you will become familiar with one of the most popular enterprise frameworks for Java and create your first Hibernate application. Never heard of Hibernate? Or maybe you've heard of it, but haven't used it? Or maybe you tried to use it, but failed? In all three cases - welcome to below the cut :) Your first Hibernate application - 1 Hello, everyone! In this article, I'll talk about the Hibernate framework's main features and help you write your first mini-application. For this, we need:
  1. IntelliJ IDEA Ultimate Edition
    Download it from the official website and activate the 30-day trial version.
  2. PostgreSQL - one of the most popular modern database management systems (DBMS)
  3. Maven (already wired up to IDEA)
  4. A little patience.
Just in case, I posted the application code on GitHub (javarush branch). The article is primarily aimed at those who have never worked with this technology before, so I've minimized the amount of code. Let's get started!

What is Hibernate?

It's one of the most popular object-relational mapping (ORM) implementations. An object-relational mapping defines the relationship between software objects and database records. Of course, Hibernate has very broad functionality, but we will focus on the simplest functions. Our goal is to create a CRUD (Create, Read, Update, Delete) application that will be able to:
  1. Create users (User), search for them in the database by ID, update their data in the database, and delete them from the database.
  2. Assign car objects (Auto) to users. Create, update, find, and delete cars from the database.
  3. Additionally, the application should automatically remove "ownerless" cars from the database. In other words, when a user is deleted, all cars belonging to that user must also be deleted from the database.
Our project will be structured like this: Your first Hibernate application - 2As you can see, nothing complicated. 6 classes + 1 file with configs. First, create a new Maven project in IntelliJ IDEA. File -> New Project. Select Maven from among the proposed project types and move to the next step. Your first Hibernate application - 3Apache Maven is a framework for automatically building projects based on a description of their structure in POM files. The entire structure of your project will be described in pom.xml, a file that IDEA itself will create in the root of your project. In the project settings, you need to specify the following Maven settings: groupId and artifactId. In projects, groupId is usually a description of the company or business unit. The domain name of the company or website can go here. In turn, artifactId is the name of the project. For groupdId, you can enter com.yourNickname.codegym. This won't have any effect on the application. For artifactId, choose any project name you like. The version can be left unchanged. Your first Hibernate application - 4On the last screen, simply confirm the previously entered data.Your first Hibernate application - 5So, we created the project. Now all that's left to do is to write some code and make it work :) First things first: if we want to create an application that works with a database, we definitely can't do without a database! Download PostgreSQL from here (I'm using version 9). PostgreSQL has a default user 'postgres' — you'll need to think up a password for it when you install. Don't forget the password. We'll need it later! (In general, using the default database in applications is bad practice, but we'll do it in order to reduce the number of ulcers causes by creating your own database). If you aren't friends with the command line and SQL queries, there's good news. IntelliJ IDEA provides an entirely suitable user interface for working with the database. It looks like this: Your first Hibernate application - 6(located on IDEA's right pane, the Database tab). To create a connection, click "+" and select our data source (PostgeSQL). Fill in the fields for the user and database ("postgres" for both of them) and enter the password that was set during installation of PostgreSQL. If necessary, download the Postgres driver. You can do this on the same page. Click "Test Connection" to verify that the database connection is established. If you see "Successful", then move on. Now we'll create the tables we need. There will be a total of two: users and autos. Parameters for the users table: Your first Hibernate application - 7Note that id is the primary key. If you don't know what the primary key is in SQL, Google it. This is important. Settings for autos table: Your first Hibernate application - 8For the autos table, you need to configure a foreign key. It will serve to link our tables. I recommend that you read more about it. Put simply, it references an external table, in our case, users. If a car belongs to the user with id = 1, then the user_id field of the autos will be equal to 1. This is how we associate users with their cars in our application. In our autos table, the user_id field will act as the foreign key. It will refer to the id field of the users table. Your first Hibernate application - 9So, we've created a database with two tables. What remains is to understand how to manage it from Java code. We will start with the pom.xml file, in which we need to include the necessary libraries (in Maven they are called dependencies). All libraries are stored in the central Maven repository. The libraries you specify in pom.xml are available for you to use in the project. Your pom.xml should look like this: Your first Hibernate application - 10Nothing complicated, as you can see. We added only 2 dependencies — for using PostgreSQL and Hibernate. Now let's move on to the Java code. Create all the necessary packages and classes in the project. To start, we need a data model: the User and Auto classes.
package models;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table (name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "name")
    private String name;
    // You can omit the Column attribute if the name property matches the column name in the table
    private int age;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Auto> autos;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
        autos = new ArrayList<>();
    }

    public void addAuto(Auto auto) {
        auto.setUser(this);
        autos.add(auto);
    }

    public void removeAuto(Auto auto) {
        autos.remove(auto);
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<Auto> getAutos() {
        return autos;
    }

    public void setAutos(List<Auto> autos) {
        this.autos = autos;
    }

    @Override
    public String toString() {
        return "models.User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package models;

import javax.persistence.*;

@Entity
@Table(name = "autos")
public class Auto {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column (name = "model")
    private String model;

    // You can omit the Column attribute if the name property matches the column name in the table
    private String color;


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    public Auto() {
    }

    public Auto(String model, String color) {
        this.model = model;
        this.color = color;
    }

    public int getId() {
        return id;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return color + " " + model;
    }
}
As you can see, classes have with a bunch of obscure annotations. Let's start digging into them. For us, the main annotation is @Entity. Read about it on Wikipedia and learn it all by heart. This is the foundation of the foundation. This annotation lets objects of your Java class be mapped to a database. For a class to be an entity, it must satisfy the following requirements:
  • It must have an empty constructor (public or protected)
  • It cannot be nested, an interface or an enum
  • It cannot be final and cannot have final fields/properties
  • It must have at least one @Id field.
Check your entity classes: they are very popular places to shoot yourself in the foot. It's very easy to forget something. Moreover, an entity can do the following:
  • It can have non-empty constructors
  • It can inherit and be inherited
  • It can have other methods and implement interfaces.
As you can see, the User class is very similar to the users table. It has id, name, and age fields. The annotations located above them don't need any particular explanation: it's clear that @Id indicates that the field is an identifier of objects of this class. The @Table annotation above the class indicates the name of the table where the objects are written. Note the comment above the age field: if the name of the field in the class is the same as the name of the table, you can omit the @Column annotation and it will work. As for the part indicated in the braces ("strategy = GenerationType.IDENTITY"): there are several strategies for generating IDs. You can Google them, but for our application, no need to bother. The main thing is that for our objects the value of id will be generated automatically. Accordingly, there is no setter for id, and we don't set it in the constructor either. However, there are some ways that the User class does stand out. It has a list of cars! Your first Hibernate application - 11The @OneToMany annotation hangs above the list. It means that several cars can correspond to the same object of the User class. The "mappedBy" element refers to the user field of the Auto class. Thus, cars and users are related. The orphanRemoval element indicates whether to apply the remove operation to entities that no longer have a relationship. If we delete a user from the database, then all cars associated with it will also be deleted. In turn, in the Auto class, you will see the user field with the @ManyToOne annotation (one User can correspond to many Autos) and the @JoinColumn annotation. It indicates which column in the autos table is used to reference the users table (i.e. the foreign key that we talked about earlier). After creating the data model, it's time to teach our program to perform operations with the data in the database. Let's start with the HibernateSessionFactoryUtil utility class. It has only one job — to create a session factory for our application to work with the database (say hello to the Factory design pattern!). It doesn't know how to do anything else.
package utils;

import models.Auto;
import models.User;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateSessionFactoryUtil {
    private static SessionFactory sessionFactory;

    private HibernateSessionFactoryUtil() {}

    public static SessionFactory getSessionFactory() {
        if (sessionFactory == null) {
            try {
                Configuration configuration = new Configuration().configure();
                configuration.addAnnotatedClass(User.class);
                configuration.addAnnotatedClass(Auto.class);
                StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
                sessionFactory = configuration.buildSessionFactory(builder.build());

            } catch (Exception e) {
                System.out.println("Исключение!" + e);
            }
        }
        return sessionFactory;
    }
}
In this class, we create a new Configuration object, and pass it the classes that it should treat as entities: User and Auto. Pay attention to the configuration.getProperties() method. What other properties are there? Where do they come from? Properties are the hibernate settings indicated in the special hibernate.cfg.xml file. Your first Hibernate application - 12Hibernate.cfg.xml is read here: new Configuration().configure(); As you see, there is nothing particularly special about it: it contains the parameters for connecting to the database, as well as the show_sql parameter. This is required so that all sql queries executed by Hibernate be displayed on the console. This way you will see exactly what Hibernate is doing at any given moment, eliminating any sense of "magic". Next we need the UserDAO class. The best practice is to program through the interfaces — create a separate UserDAO interface and UserDAOImpl implementation, but I will skip this in order to reduce the amount of code. Don't do this in real projects! The DAO (data access object) design pattern is one of the most common. The idea is simple — create an application layer responsible only for accessing data, nothing more. Fetch data from the database, update data, delete data — that's it. Study more about DAO. You'll use data access objects constantly in your work. What can our UserDao class do? Well, like all DAOs, it can only work with data. Find a user by id, update its data, delete it, get a list of all users from the database, or save a new user in the database — that's the entirety of its functionality.
package dao;

import models.Auto;
import models.User;
import org.hibernate.Session;
import org.hibernate.Transaction;
import utils.HibernateSessionFactoryUtil;
import java.util.List;

public class UserDao {

    public User findById(int id) {
        return HibernateSessionFactoryUtil.getSessionFactory().openSession().get(User.class, id);
    }

    public void save(User user) {
        Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
        Transaction tx1 = session.beginTransaction();
        session.save(user);
        tx1.commit();
        session.close();
    }

    public void update(User user) {
        Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
        Transaction tx1 = session.beginTransaction();
        session.update(user);
        tx1.commit();
        session.close();
    }

    public void delete(User user) {
        Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
        Transaction tx1 = session.beginTransaction();
        session.delete(user);
        tx1.commit();
        session.close();
    }

    public Auto findAutoById(int id) {
        return HibernateSessionFactoryUtil.getSessionFactory().openSession().get(Auto.class, id);
    }

    public List<User> findAll() {
        List<User> users = (List<User>) HibernateSessionFactoryUtil.getSessionFactory().openSession().createQuery("From User").list();
        return users;
    }
}
UserDao's methods are similar to one another. In most of them, we get a Session object (database connection session) using our Session Factory, create a single transaction within this session, perform the necessary data manipulations, save the result of the transaction in the database, and then close the session. The methods themselves, as you can see, are quite simple. DAO is the "heart" of our application. However, we won't create a DAO directly and call its methods in our main() method. All logic will be moved to the UserService class.
package services;

import dao.UserDao;
import models.Auto;
import models.User;

import java.util.List;

public class UserService {

    private UserDao usersDao = new UserDao();

    public UserService() {
    }

    public User findUser(int id) {
        return usersDao.findById(id);
    }

    public void saveUser(User user) {
        usersDao.save(user);
    }

    public void deleteUser(User user) {
        usersDao.delete(user);
    }

    public void updateUser(User user) {
        usersDao.update(user);
    }

    public List<User> findAllUsers() {
        return usersDao.findAll();
    }

    public Auto findAutoById(int id) {
        return usersDao.findAutoById(id);
    }


}
A Service is an application data layer responsible for executing business logic. If your program needs to execute some sort of business logic, it does so through services. A service contains a UserDao and calls DAO methods in its methods. It may seem like we are duplicating functions here (why not just call the methods from a DAO object?), but with lots of objects and complex logic, layering the application provides huge advantages (doing so is good practice — remember this in the future and read about "application layers"). Our service has simple logic, but service methods in real-world projects contain much more than one line of code :) Now we have everything you need to run the application! In the main() method, let's create a user and its car, associate one with the other, and save them in the database.
import models.Auto;
import models.User;
import services.UserService;

import java.sql.SQLException;

public class Main {
    public static void main(String[] args) throws SQLException {

        UserService userService = new UserService();
        User user = new User ("Jenny", 26);
        userService.saveUser(user);
        Auto ferrari = new Auto("Ferrari", "red");
        ferrari.setUser(user);
        user.addAuto(ferrari);
        Auto ford = new Auto("Ford", "black");
        ford.setUser(user);
        user.addAuto(ford);
        userService.updateUser(user);
    }
}
As you can see, the users table has its own record, and the autos table has its own record. Your first Hibernate application - 13Your first Hibernate application - 14Let's try to rename our user. Clear the users table and execute the code
import models.Auto;
import models.User;
import services.UserService;

import java.sql.SQLException;

public class Main {
    public static void main(String[] args) throws SQLException {

        UserService userService = new UserService();
        User user = new User ("Jenny", 26);
        userService.saveUser(user);
        Auto ferrari = new Auto("Ferrari", "red");
        user.addAuto(ferrari);
        Auto ford = new Auto("Ford", "black");
        ford.setUser(user);
        user.addAuto(ford);
        userService.updateUser(user);
        user.setName ("Benny");
        userService.updateUser(user);
    }
}
It works! Your first Hibernate application - 15What if you delete the user? Clear the users table (autos will clear itself) and execute the code
import models.Auto;
import models.User;
import services.UserService;

import java.sql.SQLException;

public class Main {
    public static void main(String[] args) throws SQLException {

        UserService userService = new UserService();
        User user = new User ("Jenny", 26);
        userService.saveUser(user);
        Auto ferrari = new Auto("Ferrari", "red");
        user.addAuto(ferrari);
        Auto ford = new Auto("Ford", "black");
        ford.setUser(user);
        user.addAuto(ford);
        userService.updateUser(user);
        user.setName ("Benny");
        userService.updateUser(user);
        userService.deleteUser(user);
    }
}
And our tables are completely empty (pay attention to the console — all the requests performed by Hibernate will be displayed there). You can play around with the application and try out all its functions. For example, create a user with cars, save it in the database, look at the id assigned to the user, and try to use this id in the main() method to fetch the user from the database and display a list of its cars on the console. Of course, we've only seen a small portion of Hibernate's functionality. Its capabilities are very broad, and it has long been a standard industry tool for Java development. If you want to study it in detail, I can recommend the book "Java Persistence API and Hibernate". I reviewed in a previous article. I hope this article has been helpful to readers. If you have questions, ask them in the comments. I will be happy to answer :) Also, don't forget to support the author in the contest by posting a "Like". Or better yet — "Love it" :) Good luck in your studies!