Entity in Hibernate: Mapping Java Objects to Database Structures

Entity in Hibernate: Mapping Java Objects to Database Structures

Hibernate is one of the most popular Object-Relational Mapping (ORM) frameworks in the Java environment. It enables effective mapping of Java objects to corresponding structures in relational databases. In this article, we will delve into the concept of “Entity” in Hibernate, which refers to persistent objects in a database, and discuss key concepts related to this.

What is an Entity in Hibernate?

In the context of Hibernate, an Entity is an object that represents data stored in a database. Entities are a crucial element in modelling relational data in Java-based applications. Each Hibernate entity corresponds to a specific table in the database, and instances of these entities are mapped to records in that table.

Creating an Entity Class

To make a class an entity in Hibernate, it must be annotated with @Entity. Additionally, it must have at least one field annotated with @Id, representing a unique identifier for the entity. Below is a simple example of an entity class in Hibernate:

@Entity
@Table(name = "airplane")
public class Airplane {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "airplane_id")
    private Long id;

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

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

    @Column(name = "capacity")
    private int capacity;

    // Getters and setters
}

The above example marks the class as an entity with the @Entity annotation. The id field is marked as the primary key (@Id), and the @GeneratedValue annotation informs Hibernate that the value of this field should be generated automatically. The @Table annotation allows specifying the name of the table in the database to which the entity should be mapped.

Column Mapping

The @Column annotation is used to map class fields to columns in the database table. It allows the customization of column names, data types, and other properties. In the example above, the @Column annotations are used to specify column names for the model , manufacturer , and capacity fields.

Types of Identifier Generation Strategies

Hibernate offers several strategies for generating identifiers (primary keys) for entities. The choice of the appropriate strategy depends on the developer’s preferences and the characteristics of the database. Here’s a detailed description of some commonly used identifier generation strategies in Hibernate.

1. GenerationType.IDENTITY

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "airplane_id")
private Long id;

The GenerationType.IDENTITY strategy relies on auto-increment mechanisms provided by the database. It means that the database automatically increments the primary key value with each new row. This strategy is often used for databases supporting auto-increment, such as MySQL or PostgreSQL.

2. GenerationType.AUTO

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "airplane_id")
private Long id;

The GenerationType.AUTO strategy leaves the choice of identifier generation to Hibernate. Hibernate decides whether to use auto-increment, sequences or another method available in the specific database. This strategy provides the most flexibility but may lead to different behaviours on different database systems.

3. GenerationType.SEQUENCE

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "airplane_seq")
@SequenceGenerator(name = "airplane_seq", sequenceName = "airplane_sequence", allocationSize = 1)
@Column(name = "airplane_id")
private Long id;

The GenerationType.SEQUENCE strategy involves using a sequence provided by the database. We create a sequence generator using the @SequenceGenerator annotation and then assign it to the identifier field. The allocationSize parameter specifies how many sequence values should be allocated at once. This strategy is useful for databases supporting sequences like Oracle or PostgreSQL.

4. GenerationType.TABLE

@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "airplane_gen")
@TableGenerator(name = "airplane_gen", table = "id_generator", pkColumnName = "gen_name", valueColumnName = "gen_value", allocationSize = 1)
@Column(name = "airplane_id")
private Long id;

The GenerationType.TABLE strategy involves using a special table in the database to store and manage identifiers. This table is used as an identifier generator. Parameters like pkColumnName and valueColumnName specify column names in this table. While this strategy can be used across different database systems, it is typically less efficient than sequences or auto-incrementation.

5. GenerationType.UUID

@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(name = "airplane_id", columnDefinition = "BINARY(16)")
private UUID id;

For identifiers in the form of Universally Unique Identifiers (UUIDs), the GenerationType.UUID strategy can be used. This strategy generates unique identifiers based on the standard UUID format. UUID values are 128-bit numbers that are almost certain to be unique.

Session Management in Hibernate

Hibernate introduces the concept of a session, which represents a connection to the database. The session is essential for performing operations on data, such as saving, retrieving, updating, or deleting entities.

It’s important to note that changes made to entities, such as saving, updating, or deleting, are managed within the Hibernate session. These modifications are persisted in the database only upon the successful completion and commit of the transaction. The session serves as a unit of work, allowing developers to perform operations on entities in a controlled and effective manner, ensuring that changes are applied to the database in a coherent and transactional fashion.

Here, we’ll discuss how session management works in Hibernate and demonstrate some sample operations.

Opening a Session

To interact with the database, we need to open a session. The session is typically opened through a SessionFactory, which is responsible for creating sessions. Here's an example of how to open a session:

Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sessionFactory = configuration.buildSessionFactory();

try (Session session = sessionFactory.openSession()) {
    try {
        session.beginTransaction();

        // Place your database operations here.

        session.getTransaction().commit();
    } catch (Exception e) {
        if (session.getTransaction() != null) {
            session.getTransaction().rollback();
        }
        e.printStackTrace();
    }
}

In this example, we first create a SessionFactory using a configuration file (hibernate.cfg.xml). Then, we open a new session and begin a transaction.

Saving an Entity

Once the session is open, we can perform operations on entities. Here’s an example of saving an Airplane entity:

Airplane airplane = new Airplane();
airplane.setModel("Boeing 737");
airplane.setManufacturer("Boeing");
airplane.setCapacity(150);

session.save(airplane);

In this snippet, we create a new Airplane object, configure its properties, and then use the save method within the Hibernate session. However, it's crucial to note that the save operation doesn't immediately persist the object in the database. Instead, it adds the object to the Hibernate persistence context, marking it for future database insertion. The actual database write occurs when the transaction is committed, ensuring controlled and efficient data synchronization between the persistence context and the database.

Retrieving an Entity

Retrieving an entity from the database is straightforward. Here’s an example:

Airplane retrievedAirplane = session.get(Airplane.class, 1L);
System.out.println("Retrieved Airplane: " + retrievedAirplane.getModel() + ", Capacity: " + retrievedAirplane.getCapacity());

In this snippet, we use the get method of the Hibernate session to retrieve an Airplane entity with the specified identifier (1L). The operation doesn't immediately hit the database. Instead, it looks for the entity within the Hibernate persistence context. If the entity is not found in the context, then a database query is executed to retrieve it. This approach enhances performance by minimizing unnecessary database interactions when the entity is already in the context. The retrieved entity can then be manipulated within the context, and any changes made will be synchronized with the database upon transaction commit.

Updating an Entity

Updating an entity involves modifying its properties and then using the update method of the session. Here's an example:

retrievedAirplane.setCapacity(160);
session.update(retrievedAirplane);

In this snippet, we update the capacity property of the retrieved Airplane. The update method doesn't immediately persist the changes in the database. Instead, it informs the Hibernate context about the modifications to the entity.

Deleting an Entity

To delete an entity, we use the delete method of the session. Here's an example:

session.delete(retrievedAirplane);

In this example, we delete the previously retrieved Airplane from the Hibernate context.

Force immediate database operation before transaction commit

If you want to force immediate saving of changes to the database before committing the transaction, you can use the flush method in the Hibernate session. This method synchronizes the session state with the database, but the transaction remains open. However, keep in mind that using flush too frequently may impact performance, so use it wisely.

// Modify the entity
retrievedAirplane.setCapacity(160);

// Force immediate saving of changes to the database
session.flush();

// Continue with operations
// ...

// Commit the transaction
session.getTransaction().commit();

In the above example, after modifying the airplane’s capacity, we use flush to immediately synchronize the session state with the database before committing the transaction. Remember to use flush sparingly and always close the transaction with commit after its completion.

Tips

Here are a few tips to enhance your Hibernate experience:

  • Strive to keep the size of the persistence context as small as possible to avoid excessive memory consumption. Avoid prolonged sessions that may lead to larger contexts.

  • Depending on the needs and characteristics of the application, consider using a second-level cache for entities. This can improve data access and reduce the number of queries to the database.

  • Open sessions when needed and immediately close them after completing operations. Avoid keeping sessions open for too long.

  • Start and close transactions judiciously. Short transactions can improve performance while limiting the scope of introduced changes.

  • If there’s a need to force immediate saving of changes to the database before ending the transaction, use the flush() method sparingly to avoid excessive database load.

  • Capture and handle transaction errors carefully. It’s valuable to have an exception-handling mechanism and potential transaction rollback in case of failure.

  • Optimize database calls, aiming to minimize the number of queries and operations. Understanding Hibernate’s mechanisms can help in optimization.

Applying these tips will assist in effectively managing the persistence context and sessions in Hibernate

Summary

Entities in Hibernate are a crucial element enabling the mapping of Java objects to database structures. By annotating a class as an entity with the @Entity annotation and appropriately defining the primary key and column mapping, developers can effectively integrate their objects with a database. Session management is a key aspect of working with Hibernate, allowing for controlled and efficient data operations. Thanks to Hibernate, working with databases in Java applications becomes more understandable and flexible.