Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Mapping Java Entities for Persistence in Hibernate (Part 1)

DZone 's Guide to

Mapping Java Entities for Persistence in Hibernate (Part 1)

Learn more about how to map Java entities for persistence in Hibernate.

· Java Zone ·
Free Resource

In this series of posts, we’ll explore how the domain classes in a Java application can be mapped to relational tables for persistence in a database using Hibernate. The goal is to summarize the techniques and best practices for correctly implementing the domain model of applications that need to load and store objects in an SQL data store.

To make examples easy to follow, our domain consists of simple entities representing books along with their authors, namely: a Book class, an Author class, and Publisher for representing the publisher of books. In this first part, the focus will be on basic mapping for the Book entity.

Declaring Entity Mappings

Before going through the examples, let’s review how mappings from object-oriented domain classes to relational tables are added to an application. There are two ways: (1) using annotations in Java code, and (2) using XML files using either JPA standard format or Hibernate hbm.xml files. In all the following examples, we will use annotations defined in the JPA standard and Hibernate specific annotations for features beyond what the JPA standard includes. Also, note that the target Hibernate version is 5.4 (which also supports JDK 11), but most of the details apply to either Hibernate 4.x and 5.x releases.

Basic Entity Mapping

The starting point for mapping a class is the JPA annotation @javax.persistence.Entity. It is required for enabling the persistence of the class. In addition, an identifier for the entity must be specified, mapping it to the primary key column using the @javax.persistence.Id JPA annotation. The most basic mapping is shown below.

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Book {

    @Id
    private Long id;

    ...
}


How does the identifier property (the id field) get its value? In this case, Hibernate expects you to set the value explicitly. In other words, if you try to persist a Book instance by creating it with the default constructor and calling session.save(), an exception will occur because the value of the identifier (and therefore the primary key column) is null (notice that id is of type Long, not the primitive long, which would have worked because it defaults to 0):

session.save(new Book());  // throws IdentifierGenerationException: ids for 
                           // this class must be manually assigned before
                           // calling save()


In this case, the id value must be assigned either via a constructor or a setter method. Normally, however, you want to let Hibernate automatically generate the identifier value, for example, using an auto-incremented column in the target table. This can be done using the @GeneratedValue annotation:

import javax.persistence.GeneratedValue;

@Entity
public class Book {

    @Id
    @GeneratedValue
    protected Long id;

    ...
}


The next question is: how exactly does Hibernate generate the id value? The answer is: It depends on the database vendor, or more specifically, the org.hibernate.dialect.Dialect subclass for the target database. Most of the time, it turns out to use either a sequence or an identity column as the strategy to generate the value. To be consistent from one database to another, it is best to specify the generation strategy explicitly. Hibernate provides several strategies. For example, enhanced-sequence is a generator strategy that uses a sequence to get the next value, and if the database does not support sequences, it falls back to a table simulating a sequence. Standardized generator strategies are defined in the enum javax.persistence.GenerationType. For example, to use the JPA standard sequence-based generator, we set the strategy to GenerationType.SEQUENCE:

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    protected Long id;

    ...
}


To use the more powerful Hibernate generation strategies and to gain control over generation parameters, we can leverage the Hibernate @org.hibernate.annotations.GenericGenerator annotation:

@Entity
public class Book {

    @Id
    @GeneratedValue(generator = "idGenerator")
    @org.hibernate.annotations.GenericGenerator(name = "idGenerator",
             strategy = "enhanced-sequence",
             parameters = {
                 @org.hibernate.annotations.Parameter(name = "initial_value",
                                                      value = "100")
             })
    protected Long id;

    ...
}


This causes Hibernate to use the sequence (or a table simulating it) to get the next id value, starting at value 100, i.e. the first entity instance that gets persisted would have id value 100, the next would have 101, etc.

More on Basic Entity Mapping

Excluding Columns From Persistence

By default, all fields in the annotated class get included in the persisted rows, along with the primary column mapped by the id field (actually to be specific, all fields whose types are either basic built-in Java types or embeddable types, or serializable, or fields that are mapped using associations; other fields will cause an error). Let’s say Book has a title field, then it will be automatically mapped by Hibernate:

@Entity
public class Book {

    @Id
    ...
    protected Long id;

    protected String title; // No need for mapping annotation

    ... // getter(s) and setter(s)
}


Suppose we wanted to exclude title from being persisted, we can annotate by @javax.persistence.Transient or use the transient modifier:

import javax.persistence.Transient;

@Entity
public class Book {

    ...

    @Transient
    protected String title;

    ... // getter(s) and setter(s)
}


Controlling the Column Mapping

Now, let’s suppose that we do want the title field to be persisted, but we want to control its mapped column, for example, to make it NOT NULL, or maybe to change the name of the column. To do this, we use the @Column annotation:

import javax.persistence.Column;

@Entity
public class Book {

    @Id
    ...
    protected Long id;

    // Note that TITLE is already the default column name
    @Column(name = "TITLE", nullable = false)
    protected String title;

    ...
}


Now, Hibernate would throw an exception if a Book is persisted while having a null title. Hibernate also adds a NOT NULL constraint when generating the schema using its DDL generation tool (as configured by the setting hibernate.hbm2ddl.auto).

Table Naming

Another thing we might want to control is the name of the table mapped by the class. This can be done using @Table being placed on the class:

import javax.persistence.Table; 

@Entity
@Table(name = "BOOK")
public class Book {
   ...
}


Entity Naming

The @Entity annotation also has a name element, but this controls the entity’s name, not the table name. The entity name is used queries executed using Hibernate’s supported query syntax. For example, to select a book given its title, a query would be:

Query<Book> query = session.createQuery("from Book where title = :title",
                                        Book.class);
query.setParameter("title", "some title");
Book book = query.getSingleResult();


The entity name is by default the unqualified name of the class. If we have, for whatever reason, another Book entity class in another package, then we would need to change the entity name to avoid any naming conflict in the query.

Mapping an Immutable Class

Some of the entity classes may be designed to be immutable. In this case, the annotation @org.hibernate.annotations.Immutable can be used to tell Hibernate that it should ignore any updates to the entity. It helps improve performance by excluding the class from dirty checking during persistence operations.

@Entity
@org.hibernate.annotations.Immutable
public class Book {
   ...
}


Mapping Date/Time Fields

Fields of type java.util.Date and java.util.Calendar have a special mapping requirement in JPA. Such a field needs to be annotated with @javax.persistence.Temporal specifying whether the temporal type is a date, a time, or a timestamp. Note, however, that Hibernate works without @Temporal by defaulting to a temporal type of TIMESTAMP.

Let’s say we want to add a new field containing the publishing date for a book. We probably want the date to contain only the year, the month, and the day, without any time component. This can be done using by setting the @Temporal annotation as follows:

import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Book {
    ...

    @Temporal(TemporalType.DATE)
    protected Date publishingDate;

    ...
}


This should map it to a SQL DATE type. Note that the default value of publishingDate is null. The user has to set it on the Book instance before saving it. If the field was instead creationTime representing the timestamp at which the object was inserted into the table, then we might want to have it auto-generated for us before insertion. To accomplish this, we can use the @org.hibernate.annotations.CreationTimestamp, as shown below.

@Temporal(TemporalType.TIMESTAMP)  // default in Hibernate
@CreationTimestamp
protected Date creationTime;


For an auto-generated timestamp upon an update, a similar @org.hibernate.annotations.UpdateTimestamp can be used.

Mapping Embedded Types

Some classes have semantics that makes them be part of or embedded in an entity instance, without them having an identity that makes them independent. For example, let’s say we want to have a class BookMetadata that contains some information about a book, e.g. a string containing the main topic. A Book would have a reference to a BookMetadata, but that instance does not have a separate identity or lifecycle. It is completely part of the Book object in the sense of composition in object-oriented terms. BookMetadata has essentially the same relationship as a String or a Date. We map it using an @Embeddable annotation to mark it as such:

import javax.persistence.Embeddable;

@Embeddable
public class BookMetadata {

    private String mainTopic;
    private boolean coauthored;

    ... // constructors, getters, setters
}

@Entity
public class Book {
    ...

    protected BookMetadata bookMetadata;

    ...
}


The important thing to realize on the mapped table side is that we now have one table called BOOK containing columns mapped by the Book class, plus the columns mapped by BookMetadata. Here is the generated schema:

create table BOOK (
    id bigint not null,
    coauthored boolean not null,
    mainTopic varchar(255),
    publishingDate timestamp,
    title varchar(255) not null,
    primary key (id)
)


We may want to change the name of a column in the embeddable field. To do this, we need to override the mapping of the fields of BookMetadata using the annotation @AttributeOverrideapplied on the owning entity, as shown below:

import javax.persistence.AttributeOverride;

@Entity
public class Book {

    ...

    @AttributeOverride(name = "mainTopic",
               column = @Column(name = "TOPIC", length = 60, nullable = false))
    @AttributeOverride(name = "coauthored",
               column = @Column(name = "CO_AUTHORED"))
    protected BookMetadata bookMetadata;

    ...
}


Note that we also changed the length of VARCHAR from 255 to 60 within the overridden @Column mapping for mainTopic, and made it not nullable. The resulting schema is then:

create table BOOK (
    id bigint not null,
    CO_AUTHORED boolean,
    TOPIC varchar(60) not null,
    publishingDate timestamp,
    title varchar(255) not null,
    primary key (id)
)


In the upcoming posts, we will explore more topics on entity mapping, including mapping collections, classes with inheritance, and more. Stay tuned...!

Topics:
java ,hibernate ,persistence ,mapping ,column ,entity mapping ,java entities ,java persistence

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}