Persisting application data

If you’re developing Java applications, you surely used some form of persistence to make data stay between application restarts. This is usually accomplished with the use of a database, file, or any other suitable medium.
We all know that data is often much more important than the application itself. Data is information, which is much harder to rebuild, than rebuilding an app.
The vast majority of applications, which are more advanced than “Hello World”, use some form of persistence. The standard API for database persistence in Java is the JDBC API(Java Database Connectivity). It relies on sending SQL commands directly to the relational database, through the interface which JDBC API provides. JDBC is very low level and requires a lot of boilerplate code to accomplish simple things. Firing a basic SELECT query against the database and then mapping the results of the query to Java Objects needs a significant amount of repeatable code. Now we know that when there’s boilerplate code, there’s always room for improvement. This is where Hibernate comes in.

So what is that Hibernate thing ?

To put it simply, Hibernate is a framework, which simplifies communication with relational database. It builds a layer on top of standard JDBC, helping us focus on business problems, rather than the low-level aspects of persistence. Frameworks such as Hibernate are called ORM frameworks. It stands for Object – Relational Mapping – a framework helping us map relational data into objects. That is not an easy task.
Hibernate also tends to reduce the need to know SQL. In order to use it to its full potential though, you’ll need to know how relational databases work.
Another word often bundled with Hibernate is JPA. Like most of the developers, you probably wonder how both relate to each other.

Hibernate vs JPA

JPA is an official Java specification, describing a standard, in which applications should communicate with the database. On a high level, it defines:
– How Java classes are mapped to database tables.
– How the API for basic CRUD operations looks like
– How queries are defined and mapped to objects.
If you Google for JPA implementations, you’ll find several of them. Hibernate, Eclipse Link or ObjectDb are just few examples.
Hibernate is definitely the most advanced and widely used out of all available at the moment this article is written.
Hibernate extends the standard JPA functionality through its proprietary annotations or Session API.

Retrieving data from single table – Hibernate vs JDBC example

Here’s a comparison between the standard JDBC code for retrieving data from Car table using JDBC and Hibernate JPA.
The code in both examples does exactly the same thing. It queries the database for Car with id 1 and then maps it to Car class. Take a look at how much code JDBC requires and how quick it is to accomplish the task with the use of Hibernate.

public Car getById(Long id) {
    Car car = null;
    try (Connection connection = DriverManager.getConnection(CONNECTION_STRING);
         PreparedStatement preparedStatement = connection.prepareStatement(SELECT_CARS_QUERY)) {

        preparedStatement.setLong(1, id);
        ResultSet resultSet = preparedStatement.executeQuery();

        if (resultSet.next()) {
            car = new Car();
            car.setId(resultSet.getLong(1));
            car.setMake(resultSet.getString(2));
            car.setModel(resultSet.getString(3));
            Date manufacturingDate = resultSet.getDate(4);
            car.setManufacturedAt(manufacturingDate != null ? manufacturingDate.toLocalDate() : null);
        }
    } catch (SQLException e) {
        System.out.println("Error retrieving from the database " + e.getMessage());
        throw new RuntimeException(e);
    }

    return car;
    }

And now the Hibernate version.

public Car getById(Long id){
    EntityManager entityManager = createEntityManager();
    EntityTransaction transaction = entityManager.getTransaction();
    transaction.begin();

    Car car = entityManager.find(Car.class, id);

    transaction.commit();
    entityManager.close();

    return car;
}

Advantages of an ORM framework

Even though the code in both examples does exactly the same thing, the one at the bottom is much easier to digest.
Here’s how you can benefit from using an ORM framework such as Hibernate:

  • Reduction of boilerplate code.
    As you can see in the above example, Hibernate will eliminate a large chunk of repeatable, little-value-added code. This will let you focus on the business functionality and shorten the development time.
  • Vendor independence.
    ORM frameworks, such as Hibernate attempt to enable easy switching between database vendors. When using standard JDBC, sending a

    SELECT TOP 100

    might work perfectly well on MySql, but will fail in Oracle. By eliminating the need to use native SQL, Hibernate helps you stay independent from the database you’re using.

  • Elimination of SQL.
    Hibernate will help you jump start your application coding process without extensive knowledge of SQL. Knowing the API will let you successfully complete all the basic CRUD operations. No need to go too deep into SQL at the beginning of your data persistence journey.
  • Optimizations. Using pure SQL code is definitely the most efficient. Hibernate, however, uses certain optimizations, like caching, to help your application perform better.

Why using ORM framework can sometimes be tricky ?

Even though using an ORM framework is a huge boost to productivity, it does not always come without cost. There is a fundamental mismatch between representing data in an object and relational form. Problems described below are mostly related to that fact.
First, in the relational world, concepts such as inheritance, or composition do not exist. There are few strategies which help us deal with that but they have their limitations.
Second, it’s difficult to translate complicated class hierarchies to their database representation. The performance of SQL queries, fetching such hierarchies may be unsatisfactory.
Third, issues like n+1 selects or the Cartesian product problem may appear. Often times they appear because developers are not aware of what queries are fired when data is retrieved.
Finally, it is often confusing when the data is populated to the database. Is calling persist() function always required to execute an update?

Stay tuned I will describe the challenges in more details along with approaches to deal with them in this blog.

Despite the challenges, ORM frameworks such as Hibernate, are a huge time saver, both when it comes to writing and reading code. With a few things in mind, they’re very powerful solutions in the developer’s toolkit.

2 comments add yours

    • A very cool article, thanks a million for sharing Kacper. It is a totally different view on ORMs. I was really glad to read it

Leave a Comment