Hibernate ORM

ORM, quickly

Stateful sessions

A Hibernate Session or JPA EntityManager represents a stateful persistence context. That is, it maintains a set of associated entities, automatically detects modifications made to the entities, and propagates such modifications to the database.

#1 find

The find() method fetches the entity with the given primary key from the database.

var book = session.find(Book.class, isbn)

The returned entity is associated with the persistence context, and so any modifications you make its state will be automatically propagated to the database when the session flushes. You can call evict() to remove it from the persistence context.

Optionally, find() accepts one or more FindOptions, customizing locking, timeout, or interaction with the second-level cache.

var book = session.find(Book.class, isbn,
                       LockModeType.PESSIMISTIC_WRITE,
                       CacheStoreMode.BYPASS)

The find() method even accepts an EntityGraph, allowing you to specify the associations you would like to fetch.

#2 persist

The persist() method add an entity to the persistence context, and schedules it for insertion in the database.

session.persist(book);

The actual insertion doesn’t happen until the session flushes.

#3 refresh

The refresh() method overwrites the state of the given entity with the current persistent state held by the database.

session.refresh(book)

#4 lock

The lock() method may be used to upgrade the current lock mode of an entity.

session.lock(book, LockModeType.PESSIMISTIC_FORCE_INCREMENT)

#5 merge

The merge() method merges the state of an entity which is not associated with the persistence context into the current persistence context.

var managedBook = session.merge(detachedBook);

A call to merge() returns an entity associated with the persistence context, with the same state as the entity passed as an argument to merge().

#6 remove

The remove() method schedules an entity for deletion from the database.

session.remove(book);

The actual deletion doesn’t happen until the session flushes.

#7 selection queries

Selection queries retrieve data from the database.

var books =
        session.createSelectionQuery("where title like :title order by publicationDate", Book.class)
                .setParameter("title", title);
                .setMaxResults(50)
                .getResultList();

The entities returned in the query result set are associated with the persistence context.

#8 mutation queries

Mutation queries update or delete data.

session.createMutationQuery("update Order set status = PROCESSED, processed = local datetime where status = IN_PROCESS")
        .executeUpdate();

#9 criteria queries

A criteria query is a query constructed programmatically.

var query = builder.createQuery(Book.class);
var book = query.from(Book.class);
var where = builder.conjunction();
if (titlePattern != null) {
    where = builder.and(where, builder.like(book.get(Book_.title), titlePattern));
}
if (namePattern != null) {
    var author = book.join(Book_.author);
    where = builder.and(where, builder.like(author.get(Author_.name), namePattern));
}
query.select(book).where(where)
    .orderBy(builder.asc(book.get(Book_.title)));

#10 native queries

Native queries let you write a query in the native SQL dialect of your database, map its result set to Java objects, and execute the SQL via Hibernate. In older versions of Hibernate, native SQL queries were used quite often—​but since Hibernate 6, HQL is so powerful that they’re now rarely needed.

Stateless sessions

The StatelessSession offers a simplified programming model, allowing more direct control over interaction with the database. A stateless session doesn’t have an associated persistence context, and so a change is only made to the database when you explicitly ask for it.

If you find yourself juggling the flush(), detach(), and clear() methods of a stateful session, that’s a sure indication that you might be better off going stateless.

#1 get

The get() method retrieves an entity from the database, but doesn’t associate it with any persistence context.

var book = session.get(Book.class, isbn)

Optionally, you can specify a LockMode, or an EntityGraph.

#2 insert

The insert() method directly inserts an entity in the database.

session.insert(book);

#3 update

The update() operation immediately updates the database.

session.update(book);

For a stateless session, updates to the database never happen automatically.

#4 upsert

The upsert() operation results in execution of a SQL MERGE statement.

session.upsert(book);

#5 delete

The delete() operation immediately deletes the entity from the database.

session.delete(book);

#6 fetch

The fetch() method initializes an unfetched association.

session.fetch(book.getAuthors());

For a stateless session, lazy fetching never happens transparently nor by accident.

#7 refresh

The refresh() method overwrites the state of the given entity with the current persistent state held by the database.

session.refresh(book)

#8 selection, update, criteria, and native queries

Queries work the same with a stateless session as they do in a stateful session, with one clear difference: the entities returned by the query are never associated with any persistence context.

Common mappings

Hibernate has an incredibly rich set of annotations for mapping Java classes to relational database tables, but you’ll only need a tiny fraction of them.

In fact, you’re more likely to have success with Hibernate if you avoid the use of more exotic mappings until you run into a situation where you really need one.

#1 Entity classes

An entity class is a plain Java class that maps a database table. We know when something is an entity because it’s annotated @Entity.

@Entity
class Book { ... }

You can explicitly specify the name of the mapped database table using @Table.

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

The @SecondaryTable annotation lets us map the fields of a single entity class across multiple tables.

#2 Entity inheritance

An entity may inherit a second entity.

@Entity
class Book extends Document { ... }
@Entity
class Document { ... }

By default, the two entities map the same database table. If we have a normalized database schema, we can use @Inheritance(strategy=JOINED).

@Entity
@Table(name = "books")
class Book extends Document { ... }
@Entity
@Inheritance(strategy=JOINED)
@Table(name = "documents")
class Document { ... }

A third option is @Inheritance(strategy=TABLE_PER_CLASS). You can learn more about inheritance mappings here.

#3 Basic attributes

A basic attribute is a field or property of a Java class which maps a column of a database table. Usually, the type of the field or property is a primitive type like boolean, int, double, or byte[], or a type that comes built-in to Java, like String, BigDecimal, or LocalDateTime.

String name;

You can specify the mapped column using the @Column annotation.

@Column(name="author_name")
String name;

If you have a field or property that maps to a single column, but its type isn’t one of the basic types build in to Hibernate, you can use an AttributeConverter.

#4 Identifiers

An entity must have an identifier mapping the primary key of the database table.

@Id
String isbn;

If the primary key is a surrogate key, you can let Hibernate or the database itself generate the identifier.

@Id @GeneratedValue
UUID id;

On the other hand, in a schema which uses natural keys, we often need to map a composite primary key using @EmbeddedId or @IdClass. You can read about these options here.

#5 Versions and timestamps

The @Version annotation declares a field which is used for automatic optimistic locking.

@Version
LocalDateTime lastUpdated;

#6 Associations

There’s quite a range of different kinds of association mapping, but most of the time all you need is a bidirectional one-to-many association.

@Entity
class Book {
    @Id
    String isbn;

    @ManyToOne(fetch=LAZY)
    Publisher publisher;
}
@Entity
class Publisher {
    @Id @GeneratedValue
    Long id;

    @OneToMany(mappedBy=Book_.PUBLISHER)
    List<Book> books;
}

You may use @JoinColumn to specify the foreign key mapping.

@Entity
class Book {
    @Id
    String isbn;

    @ManyToOne(fetch=LAZY)
    @JoinColumn(name = "book_publisher_id",
                referencedColumnName = "publisher_id")
    Publisher publisher;
}
@Entity
class Publisher {
    @Id @GeneratedValue
    @Column(name = "publisher_id")
    Long id;

    @OneToMany(mappedBy=Book_.PUBLISHER)
    List<Book> books;
}

You can learn all about association mappings here.

#7 Embeddable classes

Some fine-grained Java classes don’t map to their own database table. Instead, each instance of the class belongs to an entity, and the fields of the class map columns of the table mapped by the owning entity. We declare such classes using the @Embeddable annotation.

@Embeddable
class Name {
    String first;
    String last;
    Character initial;
}
@Entity
class Author {
    @Id
    String ssn;

    Name name;
}

You can read more about embeddable classes here. An embeddable class may even be mapped to a database UDT or JSON type.

#8 Enumerated types

A Java enum might map a tinyint column holding its ordinal value, or a varchar column holding its name. The @Enumerated annotation selects between the two options.

@Enumerated(STRING)
DayOfWeek dayOfWeek;

Alternatively, on MySQL, PostgreSQL, or Oracle, a Java enum might map to a database enum type.

#9 Collections and arrays

A collection or array might map a separate database table, or even a native SQL array type.

It’s easy to overuse these mappings.
Back to top