Book Image

Mastering Hibernate

Book Image

Mastering Hibernate

Overview of this book

Hibernate has been so successful since its inception that it even influenced the Java Enterprise Edition specification in that the Java Persistence API was dramatically changed to do it the Hibernate way. Hibernate is the tool that solves the complex problem of Object Relational Mapping. It can be used in both Java Enterprise applications as well as .Net applications. Additionally, it can be used for both SQL and NoSQL data stores. Some developers learn the basics of Hibernate and hit the ground quickly. But when demands go beyond the basics, they take a reactive approach instead of learning the fundamentals and core concepts. However, the secret to success for any good developer is knowing and understanding the tools at your disposal. It’s time to learn about your tool to use it better This book first explores the internals of Hibernate by discussing what occurs inside a Hibernate session and how Entities are managed. Then, we cover core topics such as mapping, querying, caching, and we demonstrate how to use a wide range of very useful annotations. Additionally, you will learn how to create event listeners or interceptors utilizing the improved architecture in the latest version of Hibernate.
Table of Contents (16 chapters)

Entity


A Hibernate entity is, typically, a sophisticated Plain Old Java Object (POJO). It is sophisticated because it represents a business model whose data is assumed to be persistent. It's always decorated with various annotations, which enable additional characteristics, among other things. Or, it is configured using an hbm Hibernate mapping XML file. When an entity contains other entities, or a collection of other entities, this implies a database association for which you have to declare the proper mapping configuration to define the relationship type.

An entity can also embed other POJOs that are not entities. In such cases, the other entities are considered value objects. They have no identity, and have little business significance on their own. (We will discuss this further when we talk about the @Embedded and @Embeddable annotations in Chapter 2, Advanced Mapping).

Entity lifecycle

You should already be familiar with entity lifecycle. However, here is a different perspective of the different phases of the lifecycle.

Before discussing the lifecycle of an entity, it is important to not think of an entity as a POJO. Instead, if you keep reminding yourself that an entity is the persistent model of business data, you will easily understand the lifecycle.

The lifecycle begins when you instantiate an entity class. At this point, the entity has no presence in the persistence context; therefore, no data has been inserted in the database and no unique ID is assigned to the new entity. At this phase of the lifecycle, the entity is said to be in the Transient state.

Once you save your new entity by calling session.save(), your entity is now in the Persistent state, because at this point the session is managing it.

What happens to the entities after the session is closed? In this case, your entity has no presence in the persistence context, but it has a presence in the database. This state is called Detached.

There is another state, which is rarely mentioned, and this is the Deleted state. When you call session.delete() on an entity, it will fire off a Delete event and internally sets the entity state to DELETED. As long as the session is open, you can still undelete the entity by calling session.persist().

There are certain lifecycle events that change the entity state, and those are well documented.

Types of entities

As mentioned earlier, you can declare a POJO class as your persistent class. There is another type of entity in Hibernate that is rarely used and perhaps not widely known, and this is map. This is known as a dynamic entity. You can use any implementation of the java.util.Map interface as a Hibernate entity. This is useful to implement a dynamic business model, which is great for the creation of a quick prototype. Ultimately, you are best off with POJO entities. If you need to implement a dynamic entity, first you can set the default entity mode to MAP:

Configuration configuration = new Configuration()
.configure()
.setProperty(Environment.DEFAULT_ENTITY_MODE,
EntityMode.MAP.toString());

Then, add a new mapping configuration. Hibernate uses the property name as a map key to get the value, for example, <property name="firstname" …/>. So, if your map contains other properties which are not included in the named map, they will be ignored by Hibernate:

<hibernate-mapping>
  <class entity-name="DynamicEntity">
    <id name="id" type="long" column="MAP_ID">
      <generator class="sequence" />
    </id>
    <property name="firstname" type="string" column="FIRSTNAME" />
    <property name="lastname" type="string" column="LASTNAME" />
  </class>
</hibernate-mapping>

Make sure that you add the new map to your Hibernate configuration. Now, you can use this as an entity. Note that when you call session.save(), you are passing the name of the entity as the first argument:

    Map<String, String> myMap = new HashMap<String, String>();
    myMap.put("firstname", "John");
    myMap.put("lastname", "Smith");
    
    Session session = HibernateUtil
   .getSessionFactory()
   .getCurrentSession();
    Transaction transaction = session.beginTransaction();

    try {
      session.save("DynamicEntity", myMap); // notice entity name
      transaction.commit();
    }
   catch (Exception e) {
      transaction.rollback();	
      // log error
    }
    finally {
      if (session.isOpen())
        session.close();
    }

Note

This used to be different in version 3.6. You didn't need to set the default entity mode on the configuration. The Session interface provided an API, which would return another session that supported dynamic entity. This was session.getSession(EntityMode.MAP), and this returned a new session, which inherited the JDBC connection and the transaction. However, this was removed in Hibernate 4.

Identity crisis

Each entity class has a property that is marked as the unique identifier of that entity. This could be a primitive data type or another Java class, which would represent a composite ID. (You'll see this in Chapter 2, Advanced Mapping, when we talk about mapping) For now, having an ID is still optional, but in future releases of Hibernate, this will no longer be the case and every entity class must have an ID attribute.

Furthermore, as Hibernate is responsible for generating and setting the ID, you should always protect it by making sure that the setter method for the ID is private so that you don't accidentally set the ID in your code. Hibernate can access private fields using reflection. Hibernate also requires entities to have a no-arg constructor.

In some cases, you have to override the equals() and hashCode() methods, especially if you are keeping the detached objects around and want to reattach them or need to add them to a set. This is because outside of the persistence context the Java equality may fail even though you are comparing two entity instances that represent the same row. The default implementation of the equals() method only checks whether the two instances are the same reference.

If you are sure that both objects have an ID assigned, then, in addition to reference equality check, you can compare their identifiers for equality. If you can't rely on the ID property but an entity can be uniquely identified by a business key, such as user ID or social security number, then you can compare the business keys.

It's not a good idea to compare all properties for equality check. There are several reasons, as follows:

  • First, if you keep a detached object around and then retrieve it again in another session, Hibernate can't tell that they are the same entities if you modify one of them.

  • Second, you may have a long list of properties and your code will look messy, and if you add a new property, you may forget to modify the equals() method.

  • Finally, this approach will lead to equal entities if you have multiple database rows with the same values, and they should be treated as different entities because they represent different rows. (For example, if two people live at the same address and one person moves, you may accidentally change the address for both.)

Beyond JPA

When you decorate your class with annotations, you are empowering your objects with additional features. There are certain features that are provided by Hibernate, which are not available in JPA.

Most of these features are provided through Hibernate annotations, which are packaged separately. Some annotations affect the behavior of your entity, and some are there to make mapping easier and more powerful.

The behavior modifying annotations that are worth noting here are as follows:

  • @Immutable: This makes an entity immutable.

  • @SelectBeforeUpdate: This is great for reducing unnecessary updates to the database and reducing contention. However, it does make an extra call through JDBC.

  • @BatchSize: This can be used to limit the size of a collection on fetch.

  • @DynamicInsert and @DynamicUpdat: These prevent null properties from being included in the dynamic SQL generation for both insert and update.

  • @OptimisticLocking: This is used to define the type of optimistic lock.

  • @Fetch: This can be used to define @FetchMode. You can instruct Hibernate to use Join, Select, or Sub Select. The Join query uses outer join to load the related entities, the Select query issues individual SQL select statements, and Sub Select is self-explanatory. (This is different from JPA's @FetchType, which is used to decide to perform lazy fetch or not.)

  • @Filter: This is used to limit the entities that are returned.

It's worth exploring both the org.hibernate.annotations JavaDocs and the JPA annotations. We will cover Hibernate annotations (beyond JPA) that modify mappings and associations in the next chapter, when we discuss mappings. We will also return to @Fetch and @Filter when we discuss Fetching in Chapter 4, Advanced Fetching.