Hibernate Tips - More Than 70 Solutions To Common Hibernate Problems
Hibernate Tips - More Than 70 Solutions To Common Hibernate Problems
ISBN: 978-1544869179
Every effort has been made in the preparation of this book to ensure the
accuracy of the information presented. However, the information contained
in this book is sold without warranty, either express or implied. The author
will not be held liable for any damages caused or alleged to be caused directly
or indirectly by this book.
Table of Contents
Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
What you get in this book. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
How to get the example project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Who this book is for. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Setting up Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
How to bootstrap Hibernate in a Java SE environment . . . . . . . . . . . . . . . . . . . 8
How to bootstrap Hibernate in a Java EE environment . . . . . . . . . . . . . . . . . . 11
How to use Hibernate’s native bootstrapping API . . . . . . . . . . . . . . . . . . . . . . . 14
How to bootstrap Hibernate with Spring Boot . . . . . . . . . . . . . . . . . . . . . . . . . . 18
How to access Hibernate APIs from JPA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
How to automatically add Metamodel classes to your project . . . . . . . . . . . . 22
Basic Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
How to define schema and table names. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
How to map basic entity attributes to database columns . . . . . . . . . . . . . . . . 28
How to map a util Date or Calendar to a database column . . . . . . . . . . . . . . . 33
How to map an enum to a database column . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
How to map a simple primary key. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
How to use an auto-incremented column to generate primary key values. 40
How to use a custom database sequence to generate primary key values . 43
How to use a database table to generate primary key values . . . . . . . . . . . . . 46
How to use a generated UUID as a primary key . . . . . . . . . . . . . . . . . . . . . . . . . 49
How to map a bidirectional many-to-one association. . . . . . . . . . . . . . . . . . . . 53
How to map an unidirectional many-to-one association . . . . . . . . . . . . . . . . . 57
How to map an unidirectional one-to-many association . . . . . . . . . . . . . . . . . 60
How to map a bidirectional many-to-many association . . . . . . . . . . . . . . . . . . 64
How to map an unidirectional many-to-many association . . . . . . . . . . . . . . . 69
How to map a bidirectional one-to-one association . . . . . . . . . . . . . . . . . . . . . 72
How to map an unidirectional one-to-one association . . . . . . . . . . . . . . . . . . . 76
Advanced Mappings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
How to map a view with Hibernate. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
How to define a custom enum mapping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
How to map the Date and Time API with Hibernate 4.4. . . . . . . . . . . . . . . . . . 86
How to map generated values. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
How to calculate entity attributes with a @Formula . . . . . . . . . . . . . . . . . . . . 92
How to cache preprocessed, non-persistent attributes. . . . . . . . . . . . . . . . . . . 95
How to automatically set an attribute before persisting it . . . . . . . . . . . . . . 100
How to order the elements of a collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
How to model a derived primary key with Hibernate . . . . . . . . . . . . . . . . . . 105
How to model an association with additional attributes . . . . . . . . . . . . . . . . 109
How to map an inheritance hierarchy to multiple tables . . . . . . . . . . . . . . . 117
How to map an inheritance hierarchy to one table . . . . . . . . . . . . . . . . . . . . 127
Hibernate Specific Queries and Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
How to join unassociated entities in a query . . . . . . . . . . . . . . . . . . . . . . . . . . 134
How to map natural IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
How to load multiple entities by their primary key . . . . . . . . . . . . . . . . . . . . 138
Java 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
How to map an association to an Optional . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
How to map classes of Java 8’s Date and Time API . . . . . . . . . . . . . . . . . . . . . 146
How to retrieve a query result as a Java 8 Stream. . . . . . . . . . . . . . . . . . . . . . 148
Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
How to log SQL statements and their parameters . . . . . . . . . . . . . . . . . . . . . . 152
How to count the executed queries in a Session . . . . . . . . . . . . . . . . . . . . . . . 156
How to use query comments to identify a query . . . . . . . . . . . . . . . . . . . . . . . 159
JPQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
How to create a JPQL query at runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
How to create a named JPQL query. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
How to select a POJO with a JPQL query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
How to select multiple scalar values with a JPQL query . . . . . . . . . . . . . . . . 170
How to initialize lazy relationships within a JPQL query . . . . . . . . . . . . . . . 172
How to downcast entities in JPQL queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
How to call a standard function in a JPQL query. . . . . . . . . . . . . . . . . . . . . . . 179
How to call a user-defined function in a JPQL query . . . . . . . . . . . . . . . . . . . 182
How to use pagination with JPQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
How to define a timeout for a JPQL query. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
How to delete multiple entities with one JPQL query . . . . . . . . . . . . . . . . . . . 188
How to update multiple entities with one JPQL query . . . . . . . . . . . . . . . . . . 190
Native SQL Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
How to create a native SQL query at runtime . . . . . . . . . . . . . . . . . . . . . . . . . 194
How to create a named native SQL query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
How to map the result of a native SQL query to entities . . . . . . . . . . . . . . . . 198
How to map the result of a native SQL query to a POJO. . . . . . . . . . . . . . . . . 201
Create queries programmatically with the Criteria API . . . . . . . . . . . . . . . . . . . 205
How to select entities with a CriteriaQuery. . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
How to select POJOs with a CriteriaQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
How to select multiple scalar values in a CriteriaQuery . . . . . . . . . . . . . . . . 213
How to call a standard database function in a CriteriaQuery . . . . . . . . . . . 216
How to call a user-defined function in a CriteriaQuery . . . . . . . . . . . . . . . . . 221
How to update multiple entities with the Criteria API . . . . . . . . . . . . . . . . . . 224
How to delete multiple entities with the Criteria API . . . . . . . . . . . . . . . . . . . 226
How to use pagination with a CriteriaQuery . . . . . . . . . . . . . . . . . . . . . . . . . . 228
How to reference entity attributes in a type-safe way . . . . . . . . . . . . . . . . . . 230
Stored Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
How to create an ad-hoc stored procedure call . . . . . . . . . . . . . . . . . . . . . . . . 236
How to call a stored procedure with a named query . . . . . . . . . . . . . . . . . . . 239
Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
How to store an entity in the second-level cache. . . . . . . . . . . . . . . . . . . . . . . 242
How to use the query cache to avoid additional queries . . . . . . . . . . . . . . . . 245
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Reviewers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Thorben Janssen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Foreword
Undoubtedly, Hibernate ORM and JPA have a steep learning curve. You
develop a quick prototype, add a few annotations to your Java classes and
everything just works --- things seem easy. But, as you try to tackle more
complex mappings or resolve performance problems, you quickly realize that
you need a deeper understanding of Hibernate to implement a complete and
efficient database access layer.
Thorben has been part of this community expert group for a long time,
helping Hibernate users via his blog posts, articles, and various forums. And
now he has written a book. And, as always, Thorben has a lot of great
Hibernate insight to share. This is the first book on Hibernate I have seen that
takes an FAQ-style approach, which is an unusual structure. Other books on
Hibernate, as well as the Hibernate documentation itself, take the same basic
approach to teaching --- they explain the individual pieces in detail,
sequentially. While this is valuable (and I’d argue critical) knowledge, it is
Hibernate Tips 1
often hard for new users to apply this sequential, segmented knowledge to
resolve more complex topics.
The FAQ approach makes it easier for users to find help on common higher-
level concepts and topics. Both forms of knowledge are useful in learning
Hibernate. Together with the other listed resources, this book will be a great
addition to every developer’s Hibernate toolbox.
Steve Ebersole
Lead Developer - Hibernate ORM
Principal Software Engineer - Red Hat, Inc.
2 Foreword
Preface
Hibernate is one of the most popular Java Persistence API (JPA)
implementations and also one of the most popular Java Object Relational
Mapping (ORM) frameworks in general. It helps you to map the classes of your
domain model to database tables and automatically generate SQL statements
to update the database on object state transitions. That is a complex task, but
Hibernate makes it look easy. You just annotate your domain classes, and
Hibernate takes care of the rest. Or, it at least seems like that in the beginning.
When you’ve used Hibernate for a while, you begin to recognize that you need
to do more than just add an @Entity annotation to your domain model classes.
Real-world applications often require advanced mappings, complex queries,
custom data types, and caching.
Hibernate can do all of that. You just have to know which annotations and
APIs to use. The acute need for this knowledge prompted me to write the
Hibernate Tips series on my Thoughts on Java [http://www.thoughts-on-java.org]
blog in 2016. In this book, you’ll find more than 35 exclusive tips and the most
popular tips from the blog.
To help you find the tip for your development task, I grouped them into the
following chapters:
Hibernate Tips 3
• The tips in the Advanced Mappings chapter show you some of Hibernate’s
advanced features and how you can use them for things like defining
custom mappings for unsupported data types, mapping of read-only
database views, defining derived primary keys, and mapping of
inheritance hierarchies.
• The tips in the JPQL chapter show you how to use JPA’s query language to
read records from the database and how you can use it to update or delete
multiple entities at once.
• If your queries are too complex for JPQL, take a look at the Native SQL
Queries chapter, which shows how to perform native SQL queries with
Hibernate.
• In the Stored Procedures chapter, I explain how you can use the
@NamedStoredProcedureQuery annotation and the StoredProcedureQuery
interface to execute stored procedures in your database.
4 Preface
How to get the example project
I use a lot of code samples in this book to show you how to solve a specific
problem with Hibernate. You can download an example project with all code
samples and executable test cases at http://www.hibernate-tips.com/
download-examples.
To get the most out of this book, you should already be familiar with the
general concepts of JPA and Hibernate. You’re in the right place if you are
looking for tips on how to use Hibernate to implement your business
requirements. I don’t explain Hibernate’s general concepts, and therefore this
book is not intended for beginners. But, if you’re already familiar with ORM
frameworks and like to learn by doing, you may find this example-based
approach helpful.
Hibernate Tips 5
6 Preface
Setting up Hibernate
You can use Hibernate in several different environments. You can use it as a
JPA implementation in a Java SE or Java EE environment, as a proprietary
persistence framework in Java SE or as a persistence provider in Spring. The
core Hibernate features that I explain in the Hibernate Tips in this book, are
available in all these environments. The only differences are features that
other frameworks in your environment provide on top of Hibernate and the
bootstrapping mechanism.
You can find examples for the different bootstrapping approaches in the
following Hibernate tips:
Hibernate Tips 7
How to bootstrap Hibernate in a
Java SE environment
Problem
I want to use Hibernate as my JPA provider in a Java SE environment. How do
I bootstrap Hibernate?
Solution
Before you can bootstrap Hibernate in your Java SE application, you need to
add the required dependencies to your classpath. I’m using Hibernate
5.2.8.Final for the examples of this book, and the hibernate-core.jar file is the
only required Hibernate dependency. The JPA jar-file is included as a
transitive dependency of hibernate-core.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.8.Final</version>
</dependency>
You also need to add a database-specific JDBC driver to the classpath of your
application. Please check your database documentation for more information.
After you’ve added the required dependencies, you can bootstrap Hibernate
as it is defined in the JPA specification. You need to add a persistence.xml file
to the META-INF directory of your application. The following code snippet
shows a simple example of a persistence.xml file. It configures a persistence-
unit with the name my-persistence-unit. It also tells Hibernate to use the
PostgreSQLDialect and to connect to a PostgreSQL database on localhost. Your
configuration might differ if you use a different database or a connection pool.
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="javax.persistence.jdbc.driver"
value="org.postgresql.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://localhost:5432/recipes" />
<property name="javax.persistence.jdbc.user"
value="postgres" />
<property name="javax.persistence.jdbc.password"
value="postgres" />
</properties>
</persistence-unit>
</persistence>
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("my-persistence-unit");
EntityManager em = emf.createEntityManager();
Hibernate Tips 9
Source Code
You can find a project with executable test cases for this Hibernate Tip in the
JPABootstrapping module of the example project. If you haven’t already done
so, you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
JPA also defines a bootstrapping approach for Java EE environments. I explain
it in How to bootstrap Hibernate in a Java EE environment.
You can also use Hibernate’s proprietary bootstrapping API, which gives you
access to proprietary configuration features. I show you how to do that in
How to use Hibernate’s native bootstrapping API.
If you want to use Hibernate with Spring Boot, take a look at How to bootstrap
Hibernate with Spring Boot.
Solution
Bootstrapping Hibernate in a Java EE environment is pretty simple. You need
to make sure that Hibernate is set up in your Java EE application server. That’s
the case if you’re using a JBoss Wildfly or JBoss EAP server. Please check your
application server documentation if you’re using a different server. Your
server might already use Hibernate as the JPA implementation or you need to
decide if you want to replace the existing JPA implementation.
You can then bootstrap Hibernate as it is defined in the JPA specification. You
just need to add a persistence.xml file to the META-INF directory of a
deployment unit. The following code snippet shows a simple example of a
persistence.xml file that defines the persistence-unit my-persistence-unit. It
tells Hibernate to use the PostgreSQLDialect and to connect to a PostgreSQL
database on localhost. Your configuration might differ if you use a connection
pool provided by your application server.
Hibernate Tips 11
<persistence>
<persistence-unit name="my-persistence-unit">
<description>Hibernate Tips</description>
<provider>
org.hibernate.jpa.HibernatePersistenceProvider
</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="javax.persistence.jdbc.driver"
value="org.postgresql.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:postgresql://localhost:5432/recipes" />
<property name="javax.persistence.jdbc.user"
value="postgres" />
<property name="javax.persistence.jdbc.password"
value="postgres" />
</properties>
</persistence-unit>
</persistence>
@PersistenceUnit
private EntityManagerFactory emf;
@PersistenceUnit
private EntityManager em;
If you want to use Hibernate with Spring Boot, take a look at How to bootstrap
Hibernate with Spring Boot.
Hibernate Tips 13
How to use Hibernate’s native
bootstrapping API
Problem
I need more control over Hibernate’s internal configuration. How do I use its
native bootstrapping API?
Solution
Hibernate’s native bootstrapping API is very flexible, which makes it more
complicated to use but also more powerful than the JPA bootstrapping API. If
you don’t need this flexibility, I recommend using the JPA API.
Before you can start the bootstrapping process, you need to add the required
dependencies to your classpath. I’m using Hibernate 5.2.8.Final for the
examples of this book, and the hibernate-core.jar file is the only required
Hibernate dependency. It also includes the JPA jar-file as a transitive
dependency.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.8.Final</version>
</dependency>
You also need to add a database-specific JDBC driver to the classpath of your
application. Please check your database documentation for more information.
As soon as you add the required dependencies, you can implement the
bootstrapping process. You need to create a StandardServiceRegistry, build a
Metadata object, and use that object to instantiate a SessionFactory.
Hibernate uses two service registries --- the BootstrapServiceRegistry and the
StandardServiceRegistry. The default BootstrapServiceRegistry provides a
good solution for most applications, so I skip the programmatic definition in
this example.
ServiceRegistry standardRegistry =
new StandardServiceRegistryBuilder()
.configure()
.build();
not recommended for production. You should use SQL scripts
instead so that you are in control of your database model and
can optimize it for your requirements.
Hibernate Tips 15
<hibernate-configuration>
<session-factory>
<property name="dialect">
org.hibernate.dialect.PostgreSQLDialect
</property>
<property name="connection.driver_class">
org.postgresql.Driver
</property>
<property name="connection.url">
jdbc:postgresql://localhost:5432/recipes
</property>
<property name="connection.username">postgres</property>
<property name="connection.password">postgres</property>
<property name="connection.pool_size">1</property>
<property name="hbm2ddl.auto">create</property>
</session-factory>
</hibernate-configuration>
SessionFactory sessionFactory =
new MetadataSources(standardRegistry)
.addAnnotatedClass(Author.class)
.buildMetadata()
.buildSessionFactory();
Session session = sessionFactory.openSession();
Source Code
You can find a project with executable test cases for this Hibernate tip in the
HibernateBootstrapping module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
The bootstrapping API defined by the JPA standard is easier to use but not as
flexible. I explain it in more detail in:
You can also use Hibernate with Spring Boot. I explain the required
bootstrapping process in How to bootstrap Hibernate with Spring Boot.
Hibernate Tips 17
How to bootstrap Hibernate with
Spring Boot
Problem
How do I use Hibernate in my Spring Boot application?
Solution
Spring Boot makes it extremely easy to bootstrap Hibernate. You just need to
add the Spring Boot JPA starter to your classpath, and Spring Boot handles the
bootstrapping for you.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
You also need to add a database-specific JDBC driver to the classpath of your
application. Please check your database documentation for more information.
spring.datasource.url = jdbc:postgresql://localhost:5432/recipes
spring.datasource.username = postgres
spring.datasource.password = postgres
If you add an H2, HSQL, or Derby database on the classpath, you can safely
omit the configuration, and Spring Boot starts and connects to an in-memory
database. You can also add multiple JDBC drivers and an in-memory database
to your classpath and use different configurations for different target
environments.
@Autowired
private EntityManager em;
Source Code
You can find a project with executable test cases for this Hibernate tip in the
SpringBootBootstrapping module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
JPA defines a bootstrapping approach for Java SE and Java EE environments. I
explain it in:
You can also use Hibernate’s proprietary bootstrapping API, which gives you
access to proprietary configuration features. I show you how to do that in
How to use Hibernate’s native bootstrapping API.
Hibernate Tips 19
How to access Hibernate APIs from
JPA
Problem
I’m using Hibernate via the EntityManager API. Is there a way to access the
proprietary Hibernate Session and SessionFactory?
Solution
Since version 2.0, JPA provides easy access to the APIs of the underlying
implementations. EntityManager and EntityManagerFactory provide an unwrap
method, which returns the corresponding classes of the JPA implementation.
In Hibernate’s case, Session and SessionFactory give you full access to
proprietary Hibernate features, such as the support for Streams and Optional.
The following code snippet shows you how to get the Hibernate Session from
EntityManager. You just need to call the unwrap method on EntityManager and
provide the Session class as a parameter.
As you can see in the next code snippet, you can get Hibernate’s
SessionFactory in a similar way. You first get EntityMangerFactory from
EntityManager and then call the unwrap method with the SessionFactory class.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AccessHibernateApi module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Hibernate Tips 21
How to automatically add
Metamodel classes to your project
Problem
I use Hibernate’s Static Metamodel Generator to generate the JPA Metamodel.
These classes are generated to a different directory, which isn’t used as a
source folder. Is there a way to automatically register this folder as a source
folder?
Solution
During my research, I learned from Frits Walraven that there is a Maven
plugin that can do exactly that. Special thanks to Frits, who also reviewed this
book.
The only thing you need to do is to add the following Maven plugin to your
build configuration. It registers a list of directories as additional source
folders. I use it in the parent pom.xml file of my project to add the directory, to
which the JPA Metamodel classes get generated (target/generated-
sources/annotations), as a source folder.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>
target/generated-sources/annotations
</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>
Source Code
You can find an example of a complete maven build configuration in the
example project. If you haven’t already done so, you can download it at
http://www.hibernate-tips.com/download-examples.
Hibernate Tips 23
Learn More
The JPA Metamodel provides a type-safe way to reference entity attributes
when you create a CriteriaQuery or an EntityGraph. I explain it in more detail
in How to reference entity attributes in a type-safe way.
Hibernate Tips 25
How to define schema and table
names
Problem
How do I define the name of the database schema and table to which
Hibernate maps an entity?
Solution
You can define the schema and table name with the schema and name attributes
of the javax.persistence.Table annotation. See a related example in the
following code snippet. You just have to add the @Table annotation to your
entity class and set the name and schema attributes.
@Entity
@Table(name = "author", schema = "bookstore")
public class Author {...}
When you now use the entity, Hibernate uses the provided schema and table
names to create the SQL statements. The following code snippet persists a new
Author entity and performs a query to get all Author entities with the given first
name.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
DefineTableAndSchemaName module of the example project. If you haven’t
already done so, you can download it at http://www.hibernate-tips.com/
download-examples.
Hibernate Tips 27
How to map basic entity attributes
to database columns
Problem
How do I map basic entity attributes to database columns?
Solution
Hibernate doesn’t need any additional information to map entity attributes of
the following Java types:
• java.lang.String
• char and java.lang.Character
• java.math.BigInteger
• java.math.BigDecimal
• java.sql.Timestamp
• java.sql.Time
• java.sql.Date
• java.util.Calendar
• java.util.Date
• java.util.Locale
• java.util.Timezone
• java.net.URL
• java.sql.Blob
• java.util.UUID
• java.sql.NClob
Since version 5.0, Hibernate also supports the classes of the Date and Time API
as basic types:
• java.time.Duration
• java.time.Instant
• java.time.LocalDateTime
• java.time.LocalDate
• java.time.LocalTime
• java.time.OffsetDateTime
• java.time.OffsetTime
• java.time.ZonedDateTime
If you want to map an entity attribute to a column with a different name, you
can annotate it with @Column and provide the column name as the name
attribute. I use this annotation in the following code snippet to map the
lastName attribute to the database column lname.
You can also use the @Column annotation to provide additional mapping
information that Hibernate can use to generate the database schema and to
apply internal optimizations. I don’t recommend using Hibernate’s schema
generation feature to create the final version of your table model, and you
shouldn’t add any additional annotations for it to your entity mappings. But,
you should tell Hibernate if a column is nullable, insertable, and updatable
because it can use this information for internal optimizations. I show that in
Hibernate Tips 29
the following example for the primary key attribute id. The mapping doesn’t
allow null as a value, and Hibernate doesn’t support updates on primary key
attributes.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(updatable = false, nullable = false)
private Long id;
@Version
private int version;
@Column(name = "lname")
private String lastName;
...
}
When you now use the entity, Hibernate uses the default mapping for all
columns that are not annotated. For all other columns, Hibernate uses the
mapping information provided by the annotation. The following code snippet
persists a new Author entity and performs a query to get the Author entity with
a given id.
...
a = em.find(Author.class, a.getId());
...
Source Code
You can find a project with executable test cases for this Hibernate tip in the
MapBasicAttributes module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Hibernate Tips 31
Learn more
Hibernate can map java.util.Date and java.util.Calendar classes without any
additional annotation. It always maps them as an SQL TIMESTAMP with
milliseconds. But that’s often not the mapping you need for your use case. I
show you how to choose a different format in How to map a util Date or
Calendar to a database column.
The same applies for enumerations. Hibernate can map them by default, but
you might want to customize the way it does it. I show you how to do that in
How to map an enum to a database column.
Solution
The SQL standard supports three different data types to store date and time
information. Hibernate can map all of them to a java.util.Date or a
java.util.Calendar. You need to decide which of the following SQL types
Hibernate will use:
• TIMESTAMP: Persists the date and time with nanoseconds. Hibernate uses
this type by default.
• DATE: Persists only the date with years, months, and days.
You can define the preferred mapping with the @Temporal annotation. As you
can see in the following code snippet, the annotation takes a TemporalType
enum as a value. The enum allows you to select the SQL type (DATE, TIME, or
TIMESTAMP) that you want to use.
@Entity
public class Author {
@Temporal(TemporalType.DATE)
private Date dateOfBirth;
...
}
Hibernate Tips 33
As you can see in the following log output, the dateOfBirth attribute of the
Author entity gets mapped to an SQL DATE without any time information.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
MapUtilDate module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Since Hibernate 5, you can also use the classes of the Java 8 Date and Time API
as entity attribute types. The new classes solve a lot of issues with the
java.util.Date and provide all information Hibernate needs to map them to
the correct JDBC types. I explain the mapping of the Date and Time API classes
in more detail in How to map classes of Java 8’s Date and Time API.
Solution
JPA and Hibernate provide two standard options to map an enum to a
database column. You can either use the String representation or the ordinal
value.
• The ordinal of an enum value is its position in the enum declaration. This
value changes and requires you to update your database when you
remove an existing value or do not add new values to the end of the enum
declaration.
You have to decide which drawback is the lesser evil for your specific
application or use an AttributeConverter to define a custom mapping to avoid
these issues.
When you use the JPA and Hibernate standard mapping, you can either rely
on the default mapping of its ordinal value or specify the mapping approach.
You can do that with an @Enumerated annotation.
The following examples use the AuthorStatus enum. This enum indicates if an
author published a book with a publisher, self-published, or is still writing a
book.
Hibernate Tips 35
public enum AuthorStatus {
@Entity
public class Author {
@Enumerated(EnumType.ORDINAL)
private AuthorStatus status;
...
}
When you use this mapping to persist an Author entity with status PUBLISHED,
Hibernate stores the value 0 in the database.
If you want to store the String representation of the enum value in the
database, you need to annotate the entity attribute with @Enumerated and set
EnumType.STRING as its value.
@Entity
public class Author {
@Enumerated(EnumType.STRING)
private AuthorStatus status;
...
}
When you now persist the same entity in the database, Hibernate writes the
value PUBLISHED into the database column status.
Learn More
You can use an AttributeConverter to define your own mapping and avoid the
drawbacks of the JPA standard mapping. I show you how to do that in How to
define a custom enum mapping.
Hibernate Tips 37
How to map a simple primary key
Problem
How do I map a simple primary key of a database table with an entity?
Solution
You can model a simple primary key with an entity attribute that you
annotate with an @Id annotation. JPA and Hibernate support the following
data types as primary keys:
• java.lang.String
• java.util.Date and java.sql.Date
• java.math.BigDecimal
• java.math.BigInteger
@Entity
public class Author {
@Id
@Column(updatable = false, nullable = false)
private Long id;
...
}
When you persist a new Author entity, you need to provide a unique primary
key value.
em.persist(a);
Source Code
You can find a project with executable test cases for this Hibernate tip in the
PrimaryKey module of the example project. If you haven’t already done so, you
can download it at http://www.hibernate-tips.com/download-examples.
Learn more
JPA and Hibernate support different strategies to generate unique primary
key values. You should use one of these strategies if the primary key value is
not provided as user input. I explain them in more detail in:
Hibernate Tips 39
How to use an auto-incremented
column to generate primary key
values
Problem
How do I use an auto-incremented database column to generate primary key
values?
Solution
JPA and Hibernate support different strategies to generate primary key values.
One of them is the identity strategy that uses an auto-incremented database
column.
If you want to use this strategy, you have to annotate the primary key
attribute with an @Id and a @GeneratedValue annotation with
GenerationType.IDENTITY as the value of the strategy attribute.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
...
}
If you persist a new Author entity, Hibernate performs the SQL INSERT
statement immediately. It uses the auto-incremented database column id to
generate the primary key value and retrieves the value from the database. You
can see that in the log file if you activate the logging for SQL statements.
log.info("Before persist");
em.persist(a);
log.info("After persist");
05:35:43,918 INFO
[org.thoughts.on.java.model.TestIdentityStrategy] - Before persist
05:35:43,975 DEBUG [org.hibernate.SQL] -
insert
into
Author
(firstName, lastName, version)
values
(?, ?, ?)
05:35:43,989 DEBUG [org.hibernate.id.IdentifierGeneratorHelper] -
Natively generated identity: 1
05:35:43,993 INFO
[org.thoughts.on.java.model.TestIdentityStrategy] - After persist
perform the INSERT statement immediately to get the primary
key value. That prevents Hibernate from using different
performance optimization techniques that rely on the
delayed execution of database operations.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
PrimaryKeyIdentityStrategy module of the example project. If you haven’t
already done so, you can download it at http://www.hibernate-tips.com/
download-examples.
Hibernate Tips 41
Learn more
Auto-incremented database columns are only one out of four options to
generate primary key values. You should also take a look at:
Solution
The JPA specification supports four options to generate primary key values.
One of them is the GenerationType.SEQUENCE, which uses a database sequence
to generate primary key values.
When you want to use a custom database sequence, you have to annotate the
primary key attribute with the @GeneratedValue annotation and set
GenerationType.SEQUENCE as the value of the strategy attribute. This tells
Hibernate to use a database sequence to generate the primary key value. If
you don’t provide any additional information, Hibernate uses its default
sequence, hibernate_sequence.
You can configure the name and schema of a custom database sequence using
a @SequenceGenerator annotation. The following code snippet shows an
example of such a mapping. The @GeneratedValue annotation references a
custom generator with the name author_generator. This generator gets defined
by the @SequenceGenerator annotation, which tells Hibernate to use the
author_seq database sequence.
Hibernate Tips 43
@Entity
public class Author {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "author_generator")
@SequenceGenerator(
name="author_generator",
sequenceName = "author_seq")
@Column(name = "id", updatable = false, nullable = false)
private Long id;
...
When you persist a new Author entity, Hibernate selects a new primary key
value from the database sequence author_seq before it executes the SQL INSERT
statement. You can see these statements in the log file, if you activate the
logging for SQL statements.
em.persist(a);
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CustomSequence module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn more
Sequences are only one out of four options to generate primary key values.
You should also have a look at:
Hibernate Tips 45
How to use a database table to
generate primary key values
Problem
How do I generate primary key values if my database doesn’t support
sequences or auto-incremented columns?
Solution
JPA and Hibernate support different strategies to generate primary key values.
One of them is the table strategy, which uses a database table to simulate a
sequence. This strategy provides a good solution if your database doesn’t
support sequences and auto-incremented database columns.
If you want to use this strategy, you have to annotate the primary key
attribute with an @Id and a @GeneratedValue annotation with
GenerationType.TABLE as the value of the strategy attribute.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
...
}
Hibernate then uses the retrieved primary key value to insert the new Author
entity into the Author table. You can see that in the log file if you activate the
logging for SQL statements.
em.persist(a);
Hibernate Tips 47
Source Code
You can find a project with executable test cases for this Hibernate tip in the
PrimaryKeyTableStrategy module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn more
Auto-incremented database columns are only one of multiple options to
generate primary key values. You should also take a look at:
Solution
Hibernate provides proprietary support for attributes of type java.util.UUID
as primary keys and offers two generators to create UUID values. The
generators support the standards IETF RFC 4122 version 4 and IETF RFC 4122
version 1.
@Entity
public class Author {
@Id
@GeneratedValue
@Column(name = "id", updatable = false, nullable = false)
private UUID id;
...
}
When you now persist a new Author entity, Hibernate generates a UUID before
writing the new record to the database.
Hibernate Tips 49
Author a = new Author();
a.setFirstName("Thorben");
a.setLastName("Janssen");
em.persist(a);
12:25:31,071 DEBUG
[org.hibernate.event.internal.AbstractSaveEventListener] -
Generated identifier: 35a18e65-97b9-48fd-a547-56f81e157253, using
strategy: org.hibernate.id.UUIDGenerator
12:25:31,113 DEBUG [org.hibernate.SQL] -
insert
into
Author
(firstName, lastName, version, id)
values
(?, ?, ?, ?)
12:25:31,117 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [1] as [VARCHAR] - [Thorben]
12:25:31,118 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [2] as [VARCHAR] - [Janssen]
12:25:31,119 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [3] as [INTEGER] - [0]
12:25:31,120 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [4] as [OTHER] - [35a18e65-97b9-48fd-a547-
56f81e157253]
Hibernate can also generate a UUID based on the IP and timestamp as defined
by IETF RFC 4122 version 1. But the configuration of it requires an additional
annotation.
@Entity
public class Book {
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator",
parameters = {
@Parameter(
name = "uuid_gen_strategy_class",
value =
"org.hibernate.id.uuid.CustomVersionOneStrategy"
)
}
)
@Column(name = "id", updatable = false, nullable = false)
private UUID id;
...
}
Hibernate uses this mapping definition in the same way as in the previous
example. It just uses a different algorithm to generate the UUID before it
stores the new Book entity in the database.
em.persist(b);
Hibernate Tips 51
05:57:35,777 DEBUG
[org.hibernate.event.internal.AbstractSaveEventListener] -
Generated identifier: c0a8b214-5abb-1aa1-815a-bbbaa51e0000, using
strategy: org.hibernate.id.UUIDGenerator
05:57:35,840 DEBUG [org.hibernate.SQL] -
insert
into
Book
(price, publishingDate, title, version, id)
values
(?, ?, ?, ?, ?)
05:57:35,845 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [1] as [DOUBLE] - [null]
05:57:35,846 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [2] as [DATE] - [null]
05:57:35,847 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [3] as [VARCHAR] - [Hibernate Tips]
05:57:35,848 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [4] as [INTEGER] - [0]
05:57:35,849 TRACE [org.hibernate.type.descriptor.sql.BasicBinder]
- binding parameter [5] as [OTHER] - [c0a8b214-5abb-1aa1-815a-
bbbaa51e0000]
Source Code
You can find a project with executable test cases for this Hibernate tip in the
PrimaryKeyUUID module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn more
Hibernate also supports different options to generate numeric primary key
values. You can read more about them in:
Solution
You need to model the association on both entities if you want to be able to
navigate it in both directions. Consider this example. A book in an online
bookstore can have multiple reviews. In your domain model, the Book entity
has a one-to-many association to the Review entity, and the Review entity has a
many-to-one relationship to the Book entity.
Let’s begin with the Review entity, which is the owning side of the association
in this example. That means that it defines the association and the Book entity
just references it. The relationship consists of two mandatory and one
optional part. The entity attribute of type Book and the @ManyToOne annotation
are required. The attribute models the association, and the annotation
declares the type of relationship. The @JoinColumn annotation is optional. It
allows you to define the name of the foreign key column. I use it in the
following code snippet to set the column name to fk_book.
Hibernate Tips 53
@Entity
public class Review {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@ManyToOne
@JoinColumn(name = "fk_book")
private Book book;
...
}
You also need to map the one-to-many association on the Book entity to make it
bidirectional. As you can see in the following code snippet, this is done in a
similar way as the many-to-one association.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@OneToMany(mappedBy = "book")
private List<Review> reviews = new ArrayList<Review>();
...
}
You need an attribute that models the association, which is the List<Review>
reviews attribute in this example and a @OneToMany annotation. Like in the table
model, the bidirectional one-to-many association gets defined on the many
side. The table on the many side stores the foreign key and its entity defines
the association. It’s similar for the entity mapping. You just need to reference
b = em.find(Book.class, 1L);
Bidirectional associations are easy to use in queries, but they also require an
additional step when you persist a new entity. You need to update the
association on both sides when you add or remove an entity. You can see an
example of it in the following code snippet, in which I first create a new Review
entity and initialize its association to the Book entity. And after that, I also need
to add the new Review entity to the List of reviews on the Book entity.
b.getReviews().add(r);
em.persist(r);
Hibernate Tips 55
@Entity
public class Book {
...
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationBidirectionalManyToOne module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Learn More
Bidirectional many-to-one associations are just one way to model
relationships between entities. I show other options in:
Solution
Using an unidirectional many-to-one association is a typical approach for
associations that contain a lot of entities on the many side of the relationship.
It allows you to navigate it in the to-one direction but avoids performance
issues that might occur if Hibernate has to load a huge number of entities to
initialize the many side of the association.
You model the association only on the entities of the many side. Let’s have a
look at an example. A book in an online bookstore can have multiple reviews.
In your domain model, you only model the many-to-one association on the
Review entity. You can see an example of such a mapping in the following code
snippet.
The association consists of two mandatory and one optional part. The entity
attribute of type Book and the @ManyToOne annotation are required. The
attribute models the association, and the annotation declares the kind of
relationship. The @JoinColumn annotation is optional. It allows you to define
the name of the foreign key column. I use it in this example to set the column
name to fk_book.
Hibernate Tips 57
If you don’t define the name yourself, Hibernate generates a name by
combining the name of the association mapping attribute and the name of the
primary key attribute of the associated entity. In this example, Hibernate
would use book_id as the default column name.
@Entity
public class Review {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@ManyToOne
@JoinColumn(name = "fk_book")
private Book book;
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationUnidirectionalManyToOne module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Hibernate Tips 59
How to map an unidirectional one-
to-many association
Problem
My table model contains a one-to-many relationship. How do I model it as
unidirectional association with Hibernate?
Solution
Modeling an unidirectional one-to-many association is not a very popular
approach. It allows you to navigate it in the to-many direction but not in the
to-one direction. Most developers prefer a bidirectional association or decide
to model an unidirectional many-to-one association to avoid performance
issues for relationships with huge numbers of entities.
You model the association only on the entities of the one side. Let’s have a look
at an example. A book in an online bookstore can have multiple reviews. In
your domain model, you only model the one-to-many association on the Book
entity. You can see an example of such a mapping in the following code
snippet. The List<Review> reviews attribute models the association and the
@OneToMany annotation declares the kind of relationship.
@Entity
public class Book {
@OneToMany
private List<Review> reviews = new ArrayList<Review>();
...
}
b.getReviews().add(r);
em.persist(r);
You can avoid that with a @JoinColumn annotation. It allows you to define the
name of the foreign key column in the table mapped by the associated entity.
In the following example, it tells Hibernate to use the fk_book column on the
review table as the foreign key.
Hibernate Tips 61
@Entity
public class Book {
@OneToMany
@JoinColumn(name = "fk_book")
private List<Review> reviews = new ArrayList<Review>();
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationUnidirectionalOneToMany module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Hibernate Tips 63
How to map a bidirectional many-
to-many association
Problem
My table model contains a many-to-many association. How do I model it with
Hibernate so that I can navigate it in both directions?
Solution
You need to model the association on both entities if you want to be able to
navigate it in both directions. Let’s have a look at an example. Multiple
Authors can write multiple books and a book can be written by one or more
authors. That’s a typical many-to-many association, and you probably want to
navigate it in both directions in your domain model and queries. You need to
model it as a many-to-many association on the Book entity and the Author
entity.
Let’s begin with the Book entity, which is the owning side of the association in
this example. That means that it defines the association and the Author entity
just references it.
The relationship definition consists of two mandatory and one optional part.
The entity attribute List<Author> authors and the @ManyToMany annotation are
required. The attribute models the association, and the annotation declares
the kind of relationship. The @JoinTable annotation is optional. It allows you to
define the name of the join table and foreign key columns that store the many-
to-many association. I use it in the following code snippet to set the name of
the join table to book_author and the names of the foreign key columns to
fk_book and fk_author.
If you don’t define the name yourself, Hibernate generates default table and
column names. The default table name is the combination of both entity
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@ManyToMany
@JoinTable(
name = "book_author",
joinColumns = { @JoinColumn(name = "fk_book") },
inverseJoinColumns = { @JoinColumn(name = "fk_author") })
private List<Author> authors = new ArrayList<Author>();
...
}
You also need to map the many-to-many association on the Author entity to
make it bidirectional. As you can see in the following code snippet, this is done
in a similar way as on the Book entity. You need an attribute that models the
association and a @ManyToMany annotation. In this example, it’s the List<Book>
books attribute that I annotated with a @ManyToMany annotation. The association
is already defined on the Book entity. You can therefore just reference the
attribute on the Book entity in the mappedBy attribute and Hibernate uses the
same definition.
Hibernate Tips 65
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@ManyToMany(mappedBy="authors")
private List<Book> books = new ArrayList<Book>();
...
}
b = em.find(Book.class, 1L);
Bidirectional associations are easy to use in queries, but they also require an
additional step when you persist a new entity. You need to update the
association on both sides when you add or remove an entity. You can see an
example of it in the following code snippet in which I first create a new Author
entity and add the Book entity to the List of books. And after that, I also need to
add the new Author entity to the List of authors on the Book entity.
a.getBooks().add(b);
b.getAuthors().add(a);
em.persist(a);
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@ManyToMany(mappedBy="authors")
private List<Book> books = new ArrayList<Book>();
...
}
Hibernate Tips 67
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationBidirectionalManyToMany module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Learn More
Bidirectional many-to-many associations are just one way to model
relationships between entities. I show other options in:
Solution
You only need to model the association on the entity from which you want to
navigate the relationship. Let’s have a look at an example. Multiple Authors
can write multiple books, and a book can be written by one or more authors.
That’s a typical many-to-many association. If you only want to navigate it from
the Book to the Author entities, you only need to model it as a many-to-many
association on the Book entity.
The relationship definition consists of two mandatory and one optional part.
The entity attribute List<Author> authors and the @ManyToMany annotation are
required. The attribute models the association, and the annotation declares
the kind of relationship. The @JoinTable annotation is optional. It allows you to
define the name of the join table and foreign key columns that store the many-
to-many association. I use it in the following code snippet to set the name of
the join table to book_author and the names of the foreign key columns to
fk_book and fk_author.
If you don’t define the names yourself, Hibernate generates default table and
column names. The default table name is the combination of both entity
names. In this example, it would be Book_Author. The foreign key column is
generated by combining the name of the association mapping attribute and
the name of the primary key attribute of the entity. These would be books_id
and authors_id in this example.
Hibernate Tips 69
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@ManyToMany
@JoinTable(
name = "book_author",
joinColumns = { @JoinColumn(name = "fk_book") },
inverseJoinColumns = { @JoinColumn(name = "fk_author") })
private List<Author> authors = new ArrayList<Author>();
...
}
b = em.find(Book.class, 1L);
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationUnidirectionalManyToMany module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Hibernate Tips 71
How to map a bidirectional one-to-
one association
Problem
My table model contains a one-to-one association. How do I model it with
Hibernate so that I can navigate it in both directions?
Solution
You need to model the association on both entities if you want to be able to
navigate it in both directions. Let’s have a look at an example. A book gets
created from a manuscript. You could model this with a Book and a Manuscript
entity and a one-to-one association between them. You need to model that
with a one-to-one association on the Book entity and the Manuscript entity.
Let’s begin with the Manuscript entity, which is the owning side of the
association in this example. That means that it defines the relationship and
the Book entity just references it.
The relationship definition consists of two mandatory and one optional part.
The entity attribute Book book and the @OneToOne annotation are required. The
attribute models the association, and the annotation declares the kind of
relationship. The @JoinColumn annotation is optional. It allows you to define
the name of the foreign key column that links the book to the manuscript. I
use it in the following code snippet to set the name of the foreign key column
to fk_book. If you don’t define the name yourself, Hibernate generates a name
by combining the name of the association mapping attribute and the name of
the primary key attribute of the associated entity. In this example, Hibernate
would use book_id as the default column name.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@OneToOne
@JoinColumn(name = "fk_book")
private Book book;
...
}
You also need to map the one-to-one association on the Book entity to make it
bidirectional. As you can see in the following code snippet, this is done in a
similar way as the one-to-one association on the Manuscript entity. You need an
attribute that models the association and a @OneToOne annotation. In this
example, it’s the Manuscript manuscript attribute, which I annotated with a
@OneToOne annotation. The association is already defined on the Manuscript
entity. You can therefore just reference the attribute on the Book entity in the
mappedBy attribute and Hibernate uses the same definition.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@OneToOne(mappedBy = "book")
private Manuscript manuscript;
...
}
Hibernate Tips 73
That’s all you need to do to define a bidirectional one-to-one association. You
can now navigate it in both directions in your JPQL or Criteria API queries or
on your domain objects.
Bidirectional associations are easy to use in queries, but they also require an
additional step when you persist a new entity. You need to update the
association on both sides when you add or remove an entity. You can see an
example of it in the following code snippet in which I first create a new
Manuscript entity and initialize its association to the Book entity. And after that,
I also need to set the new Manuscript entity on the Book entity.
b.setManuscript(m);
em.persist(m);
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationBidirectionalOneToOne module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Hibernate Tips 75
How to map an unidirectional one-
to-one association
Problem
My table model contains an one-to-one association. I only need to navigate it
one direction. How do I model that with Hibernate?
Solution
You only need to model the association on the entity from which you want to
navigate the relationship. Let’s have a look at an example. A book gets created
from a manuscript. You could model this with a Book and a Manuscript entity
and a one-to-one association between them. If you just want to navigate it
from the Manuscript to the Book entity, you only need to model it as a one-to-
one association on the Manuscript entity.
The relationship definition consists of two mandatory and one optional part.
The entity attribute Book book and the @OneToOne annotation are required. The
attribute models the association, and the annotation declares the kind of
relationship. The @JoinColumn annotation is optional. It allows you to define
the name of the foreign key column that links the book to the manuscript. I
use it in the following code snippet to set the name of the foreign key column
to fk_book. If you don’t define the name yourself, Hibernate generates a name
by combining the name of the association mapping attribute and the name of
the primary key attribute of the associated entity. In this example, Hibernate
would use book_id as the default column name.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@OneToOne
@JoinColumn(name = "fk_book")
private Book book;
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationUnidirectionalOneToOne module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Hibernate Tips 77
Learn More
Unidirectional one-to-one associations are just one way to model relationships
between entities. I show other options in:
• How to map the Date and Time API with Hibernate 4.4
Hibernate Tips 79
How to map a view with Hibernate
Problem
I have a read-only view that I want to use in associations and JPQL queries.
How do I map it with Hibernate?
Solution
Database views, in general, are mapped in the same way as database tables.
You just have to define an entity that maps the view with the specific name
and one or more of its columns.
But the normal table mapping is not read-only, and you can use the entity to
change its content.
Depending on the database you use and the definition of the view, you’re not
allowed to perform an update on the view content. You should therefore also
prevent Hibernate from updating it.
You can easily achieve that by annotating your entity with Hibernate’s
@Immutable annotation.
@Entity
@Immutable
public class BookView {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
@Column(name = "version")
private int version;
@Column
private String title;
@Column
private String authors;
...
As a result, Hibernate performs an SQL SELECT statement to read the entity but
it does not perform any UPDATE statements when you change an attribute.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
DatabaseViews module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Hibernate Tips 81
How to define a custom enum
mapping
Problem
I need to map enum values from a legacy database that don’t match the enum
standard mapping.
Or:
I don’t want to face the drawbacks of JPA’s standard enum mapping. How do I
avoid them?
Solution
Since JPA 2.1, you can use an AttributeConverter to implement a custom
mapping for your enums. It allows you to implement the conversion between
a Java type and its database representation. You can use it to convert all basic
attributes defined by entity classes, mapped superclasses, or embeddable
classes. The only exceptions are id attributes, version attributes, relationship
attributes and attributes annotated as @Temporal or @Enumerated.
You also need to annotate the class with the @Converter annotation. The
autoApply attribute of the @Converter annotation defines if Hibernate shall
apply the AttributeConverter to all entity attributes of the given type. That is a
good approach if you want to define a custom mapping for your enum values.
If you don’t want to use the AttributeConverter for all entity attributes of type
AuthorStatus, you can set the autoApply attribute to false. You then need to
activate the converter for specific attributes. You can do that by annotating the
entity attribute with a @Convert(converter = AuthorStatusConverter.class)
annotation that references the class of the AttributeConverter you want to use.
@Converter(autoApply = true)
public class AuthorStatusConverter implements AttributeConverter
<AuthorStatus, String> {
@Override
public String convertToDatabaseColumn(AuthorStatus status) {
switch (status) {
case NOT_PUBLISHED:
return "N";
case PUBLISHED:
return "P";
case SELF_PUBLISHED:
return "S";
default:
throw new IllegalArgumentException(
"AuthorStatus ["+status+"] not supported.");
}
}
Hibernate Tips 83
@Override
public AuthorStatus convertToEntityAttribute(String dbData) {
switch (dbData) {
case "N":
return AuthorStatus.NOT_PUBLISHED;
case "P":
return AuthorStatus.PUBLISHED;
case "S":
return AuthorStatus.SELF_PUBLISHED;
default:
throw new IllegalArgumentException(
"AuthorStatus ["+dbData+"] not supported.");
}
}
}
The autoApply attribute makes this AttributeConverter easy to use. You just
need to define an entity attribute of type AuthorStatus without a @Enumerated
annotation, and Hibernate applies the conversion automatically.
@Entity
public class Author {
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CustomEnumerationsMapping module of the example project. If you haven’t
already done so, you can download it at http://www.hibernate-tips.com/
download-examples.
Custom enum mappings are just one use case for an AttributeConverter. You
can also use it to map the classes of Java 8’s Date and Time API with older
Hibernate versions, as I show in How to map the Date and Time API with
Hibernate 4.4.
Hibernate Tips 85
How to map the Date and Time API
with Hibernate 4.4
Problem
Hibernate 4.4 doesn’t support the classes of Java 8’s Date and Time API as
attribute types. How do I persist these classes with Hibernate?
Solution
Since JPA 2.1, you can use an AttributeConverter to easily implement a custom
mapping for unsupported data types. It allows you to implement the
conversion between a Java type and its database representation. You can use
it to convert all basic attributes defined by entity classes, mapped
superclasses, or embeddable classes. The only exceptions are id attributes,
version attributes, relationship attributes and attributes annotated
as @Temporal or @Enumerated.
You also need to annotate the class with the @Converter annotation. Its
autoApply attribute defines if Hibernate shall apply the AttributeConverter to
all entity attributes of the given type. That is a good approach if you want to
define custom mappings for the classes of the Date and Time API.
86 How to map the Date and Time API with Hibernate 4.4
If you don’t want to use the AttributeConverter for all entity attributes of type
LocalDate, you can set the autoApply attribute to false. You then need to
activate the converter for specific attributes. You can do that by annotating the
entity attribute with a @Convert(converter = LocalDateConverter.class)
annotation that references the class of the AttributeConverter you want to use.
@Converter(autoApply = true)
public class LocalDateConverter
implements AttributeConverter<LocalDate, Date> {
@Override
public Date convertToDatabaseColumn(LocalDate attribute) {
return Date.valueOf(attribute);
}
@Override
public LocalDate convertToEntityAttribute(Date dbData) {
return dbData.toLocalDate();
}
}
The autoApply attribute makes this AttributeConverter easy to use. You just
need to define an entity attribute of type java.time.LocalDate and Hibernate
will applies the conversion automatically.
@Entity
public class Author {
...
}
Hibernate Tips 87
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AttributeConverterForDateAndTime module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Learn More
Hibernate 5 supports the classes of the Date and Time API as BasicType. I show
you how to use them in How to map classes of Java 8’s Date and Time API.
88 How to map the Date and Time API with Hibernate 4.4
How to map generated values
Problem
My database administrator set up a trigger to generate the value of a database
column. How do I map this column so that Hibernate retrieves the value after
it gets generated?
Solution
You can annotate an entity attribute with @Generated(GenerationTime value), to
tell Hibernate that the database will generate the value of the attribute. The
GenerationTime enum tells Hibernate when the database will generate the
value. It can either do this NEVER, only on INSERT or ALWAYS (on insert and
update). Hibernate then executes an additional query to retrieve the
generated value from the database.
@Entity
public class Author {
@Column
@Generated(GenerationTime.ALWAYS)
private LocalDateTime lastUpdate;
...
As you can see in the log output, Hibernate now performs an additional query
for each insert and update statement to retrieve the generated value.
Hibernate Tips 89
// Transaction 1
em.getTransaction().begin();
em.getTransaction().commit();
log.info(a);
// Transaction 2
em.getTransaction().begin();
a = em.find(Author.class, a.getId());
a.setFirstName("Changed Firstname");
em.getTransaction().commit();
log.info(a);
Source Code
You can find a project with executable test cases for this Hibernate tip in the
MapGeneratedColumns module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Hibernate Tips 91
How to calculate entity attributes
with a @Formula
Problem
The value of one of the entity attributes needs to be calculated by a SQL
expression. How do I map that with Hibernate?
Solution
You can use the @Formula annotation to provide a SQL snippet. Hibernate
executes it when it fetches the entity from the database. The return value of
the SQL expression gets mapped to a read-only entity attribute.
The following examples shows a @Formula that calculates the age of an author.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Column
private LocalDate dateOfBirth;
...
Source Code
You can find a project with executable test cases for this Hibernate tip in the
Formula module of the example project. If you haven’t already done so, you
can download it at http://www.hibernate-tips.com/download-examples.
Hibernate Tips 93
Learn more
If you don’t want to execute the SQL snippet every time Hibernate loads the
entity, you should make it a part of a custom query. This allows you to execute
it only when you need it.
Here are a few tips that help you to define complex queries:
Solution
There are different ways to provide a calculated value to the user of the entity:
1. You can use a @Formula to provide an SQL expression that returns the
value.
2. You can use field access and calculate the value in a getter method.
3. You can use a transient entity attribute that stores the calculated value
without persisting it in the database.
This approach requires you to calculate the value for each call of the getAge()
method. It is, therefore, not a good solution for complex calculations that you
need to perform often.
Hibernate Tips 95
@Entity
public class Author {
...
@Column
private LocalDate dateOfBirth;
When you use this mapping, Hibernate doesn’t select the value of the age
attribute from the database and calculates it every time the getAge method
gets called.
Hibernate Tips 97
@Entity
public class Author {
...
@Column
private LocalDate dateOfBirth;
@Transient
private Integer age;
...
return age;
}
}
As you can see in the following example, Hibernate doesn’t select the value of
the age attribute from the database and it only gets calculated for the first call
of the getAge method.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
TransientAttributes module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn more
The @Formula annotation provides another option to calculate entity attribute
values based on other database columns. You can read more about it in: How
to calculate entity attributes with a @Formula.
Hibernate Tips 99
How to automatically set an
attribute before persisting it
Problem
I want to initialize an entity attribute automatically before it gets persisted.
How do I execute custom code before Hibernate persists an entity?
Solution
The JPA specification defines a set of callback annotations to trigger method
calls for certain lifecycle events. If you want to initialize an entity attribute
before it gets persisted, you just have to do 2 things:
You can see an example of such a method in the following code snippet.
@Entity
public class Author {
...
@PrePersist
private void initializeCreatedAt() {
this.createdAt = LocalDateTime.now();
log.info("Set createdAt to "+this.createdAt);
}
}
Hibernate calls this method before it persists the new Author entity and
trigger the initialization of the createdAt attribute. You can see that in the log
output, when you persist a new Author entity.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
PrePersistLifecycleEvent module of the example project. If you haven’t
already done so, you can download it at http://www.hibernate-tips.com/
download-examples.
Solution
JPA supports the @OrderBy annotation. You can use it to define the order in
which associated entities shall be retrieved from the database.
query, and you should make sure that you always need to
retrieve the entities in the defined order. Otherwise, you
should use a use case-specific query that returns the entities
in the defined order.
You can apply the @OrderBy annotation to a relationship attribute and define
the ordering in the same way as in a JPQL query. You have to provide one or
more entity attributes as a comma-separated list. Hibernate applies an
ascending order by default. But you can also define the preferred order for
each of the attributes. You just need to add ASC to define an ascending order or
DESC for an descending order behind the attribute name.
I use this annotation in the following code snippet to retrieve the Authors of a
Book in the ascending order of their lastName attribute.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
OrderRelationships module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Solution
You can model a derived primary key with an embedded id and a @MapsId
annotation on the association whose primary key is part of the derived
identifier. The following code snippet shows an example of such a mapping.
@Entity
public class Review {
@EmbeddedId
private ReviewId id;
@ManyToOne
@JoinColumn(name = "fk_book")
@MapsId("bookId")
private Book book;
...
}
The id attribute models the primary key of the Review entity. It is of type
ReviewId, which is an embeddable and shown in the following code snippet.
The second important part of this mapping is the @MapsId annotation on the
Book book association. You can use this annotation with many-to-one and one-
to-one associations. It tells Hibernate to use the primary key of the to-one side
of the association as a primary key attribute of the to-many side of the
You can see the ReviewId class in the following code snippet. It models the
primary key with the attributes bookId and userName. It is used as an embedded
id and needs to fulfill the requirements of a primary key class. A primary key
class must be public, have a public default constructor, be serializable and
implement the equals and hashCode methods.
@Embeddable
public class ReviewId implements Serializable {
public ReviewId() {
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((bookId == null) ? 0 : bookId.hashCode());
result = prime * result
+ ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
The following code snippet shows an example that persists a new Review
entity. Due to the @MapsId annotation, Hibernate automatically initializes the
primary key attribute bookId when you call the setBook(Book b) method. That
requires you to initialize the id attribute before you set the Book association.
r.setComment("This is a comment");
em.persist(r);
The ReviewId class only contains the bookId attribute of type Long and not the
association to the Book entity. This makes it easy to instantiate a new ReviewId
object, if you want to lookup a Review entity.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
DerivedPrimaryKey module of the example project. If you haven’t already done
so, you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Derived primary keys are often used by entities that model the join tables of
many-to-many associations. I use one in the Hibernate tip How to model an
association with additional attributes.
Solution
Modelling a many-to-many association with JPA and Hibernate is simple. You
just need an entity attribute and a @ManyToMany annotation. Hibernate
internally maps the relationship to the join table you need in your table
model. But this approach has one downside. It doesn’t allow you to map any
columns of the join table. If you want to do that, you need to model it in the
same way as you do it in the database. You need to split the many-to-many
association into an entity that maps the join table and has two many-to-one
relationships to the associated entities.
The following diagram shows the domain model of a bookstore that uses an
association with additional attributes. There are books in multiple formats
(e.g. hardcover, paperback, ebook) and each format was published by a
different publisher. The Book and Publisher entities model the two main
domain objects. The BookPublisher entity models the association between
them and persists the Format as an additional attribute.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
private int version;
@OneToMany(mappedBy = "book")
private List<BookPublisher> publishers =
new ArrayList<BookPublisher>();
...
}
@Entity
public class Publisher {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
private int version;
@OneToMany(mappedBy = "publisher")
private List<BookPublisher> books =
new ArrayList<BookPublisher>();
...
}
@Entity
public class BookPublisher {
@EmbeddedId
private BookPublisherId id;
@Enumerated(EnumType.STRING)
private Format format;
@ManyToOne
@JoinColumn(name = "fk_book")
@MapsId("bookId")
private Book book;
@ManyToOne
@JoinColumn(name = "fk_publisher")
@MapsId("publisherId")
private Publisher publisher;
...
}
@Embeddable
public class BookPublisherId implements Serializable {
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((bookId == null) ? 0 : bookId.hashCode());
result = prime * result
+ ((publisherId == null) ? 0 : publisherId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BookPublisherId other = (BookPublisherId) obj;
When you want to persist the association between a Book and a Publisher
entity, you need to create a new BookPublisher entity and associate it with a
Book and a Publisher entity.
em.persist(bp);
Source Code
You can find a project with executable test cases for this Hibernate tip in the
AssociationsWithAttributes module of the example project. If you haven’t
already done so, you can download it at http://www.hibernate-tips.com/
download-examples.
Solution
JPA and Hibernate support different inheritance strategies that allow you to
map the entities to different table structures. The MappedSuperclass approach
and the strategies TablePerClass and Joined map an inheritance hierarchy of
entities to multiple tables. Each of them maps the entities to a different table
structure with its advantages and disadvantages. You need to decide for your
particular use case which approach you want to use.
Let’s take a look at the entity model that I use in all examples of this Hibernate
tip before I show you the different inheritance strategies. Authors can write
different kinds of Publications, like Books and BlogPosts. The Publication class
is the super class of the Book and BlogPost classes.
@MappedSuperclass
public abstract class Publication {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private int version;
...
}
The subclasses only need to extend the superclass and are annotated with a
@Entity annotation. The mapping of the inheritance hierarchy doesn’t require
any additional annotations.
@Entity
public class Book extends Publication {
...
}
TablePerClass
The TablePerClass strategy maps each concrete entity class of the hierarchy,
including the superclass, to its own database table. In contrast to the
MappedSuperclass approach, the superclass is now also an entity which gets
mapped to a database table and can be used in queries and associations.
If you want to use this inheritance strategy, you need to annotate the
superclass with an @Inheritance annotation and provide the
InheritanceType.TABLE_PER_CLASS as the value of the strategy attribute.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Publication {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private int version;
@ManyToMany
@JoinTable(
name="PublicationAuthor",
joinColumns={@JoinColumn(name="publicationId",
referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="authorId",
referencedColumnName="id")})
private Set<Author> authors = new HashSet<Author>();
...
}
The subclasses only need to extend the superclass and are annotated with a
@Entity annotation.
...
}
The TablePerClass strategy allows efficient queries as long as you select only
one kind of entity. But as you can see in the following log output, polymorphic
queries and associations require complex join statements and should be
avoided.
Joined
The Joined strategy maps each entity of the hierarchy, including the
superclass, to a database table. But in contrast to the TablePerClass approach,
the table of the superclass contains all columns shared by the subclasses. That
makes the book and blogpost table of the current example a lot smaller. They
only consist of a primary key column and a column to persist the additional
attribute of each entity.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Publication {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private int version;
@ManyToMany
@JoinTable(name="PublicationAuthor",
joinColumns={@JoinColumn(name="publicationId",
referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="authorId",
referencedColumnName="id")})
private Set<Author> authors = new HashSet<Author>();
...
}
@Entity
public class Book extends Publication {
...
}
The Joined table strategy maps the entity attributes defined by the superclass
to columns in the publication table and the attributes defined by the Book or
BlogPost entity to columns in the book or blogpost table. That makes the tables
of the subclasses smaller and polymorphic queries easier. But all select
statements require at least one JOIN clause that joins the table of the
superclass with the table of the selected subclass.
TypedQuery<Book> q = em.createQuery(
"SELECT b FROM Book b WHERE b.id = :id", Book.class);
q.setParameter("id", 1L);
b = q.getSingleResult();
Learn more
You can also map all entities of the inheritance hierarchy to the same database
table. I show you how to do that in How to map an inheritance hierarchy to
one table.
Solution
JPA and Hibernate support different inheritance strategies which allow you to
map the entities to different table structures. The SingleTable strategy is one
of them and maps an inheritance hierarchy of entities to a single database
table.
Let’s have a look at the entity model before I explain the details of the
SingleTable strategy. Authors can write different kinds of Publications, like
Books and BlogPosts. The Publication class is the super class of the Book and
BlogPost classes.
If you want to use this inheritance strategy, you need to annotate the
superclass with an @Inheritance annotation and provide the
InheritanceType.SINGLE_TABLE as the value of the strategy attribute.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Publication {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private int version;
@ManyToMany
@JoinTable(name="PublicationAuthor",
joinColumns={@JoinColumn(name="publicationId",
referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="authorId",
referencedColumnName="id")})
private Set<Author> authors = new HashSet<Author>();
...
}
The subclasses need to extend the superclass, and you need to annotate them
with a @Entity annotation. The JPA specification also recommends to annotate
it with a @DiscriminatorValue annotation to define the discriminator value for
this entity class. If you don’t provide this annotation, your JPA implementation
generates a discriminator value. But the JPA specification doesn’t define how
to generate the discriminator value, and your application might not be
portable to other JPA implementations. Hibernate uses the simple entity name
as the discriminator.
...
}
Learn more
You can also map the entities of the inheritance hierarchy to multiple
database tables. I show you how to do that in How to map an inheritance
hierarchy to multiple tables.
If you’re using Hibernate 5, you should also have a look at the chapter about
the Java 8 support in Hibernate. It shows some additional extensions to the
JPA standard.
Solution
Hibernate extends JPQL’s limited join feature with the proprietary support for
joins of unassociated entities. You can see an example of such a JOIN clause in
the following code snippet.
Hibernate uses the same syntax as you probably know from SQL. You
reference the two entities you want to join and the kind of join you want to
perform. Hibernate supports an inner JOIN, a LEFT outer join and a RIGHT outer
join. You also need to define a join condition in the ON clause of the JOIN clause.
The previous code snippet shows an inner join of all Book and Review entities
so that I can count the number of reviews for each book. When you execute
this query, Hibernate transforms the JPQL JOIN clause into a similar looking
SQL JOIN clause.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
HibernateJoinUnassociatedEntities module of the example project. If you
haven’t already done so, you can download it at http://www.hibernate-
tips.com/download-examples.
Solution
Hibernate provides proprietary support for natural IDs. It allows you to model
them as a natural identifier of an entity and provides an additional API for
retrieving them from the database.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@NaturalId
private String isbn;
...
}
Natural IDs are immutable by default. If you need mutable, natural identifier,
you have to set the mutable attribute of the @NaturalId annotation to true.
Book b = session.byNaturalId(Book.class)
.using(Book_.isbn.getName(), "123-4567890123")
.load();
You have to provide the class or the name of the entity as a parameter to the
byNaturalId method to tell Hibernate which entity you want to retrieve. The
following call of the using method provides the name of the natural ID
attribute and its value. If the natural ID consists of multiple attributes, you
have to call this method for each part of the ID. In this example, I use the JPA
metamodel to reference the name of the isbn attribute in a type-safe way.
After you’ve provided the value of the natural id, you can call the load,
getReference or loadOptional method to get the entity identified by it.
When your natural identifier consists of only one entity attribute, you can also
use the bySimpleNaturalId method to load it. As you can see in the following
code snippet, it provides a more convenient way to load entities with simple
natural ids.
Book b = session.bySimpleNaturalId(Book.class)
.load("123-4567890123");
Source Code
You can find a project with executable test cases for this Hibernate tip in the
HibernateNaturalId module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Solution
Loading multiple entities by their primary keys is a very common use case.
Since version 5.1, Hibernate offers a proprietary API that makes it a lot easier
to load multiple entities and provides additional benefits, like fetching huge
lists in multiple batches.
MultiIdentifierLoadAccess<Book> multi =
session.byMultipleIds(Book.class);
List<Book> books = multi.multiLoad(1L, 2L, 3L);
The call of the multiLoad method in this example loads the three Book entities
with the given primary keys. Hibernate creates one query for this method call
and provides the three primary keys as parameters to an IN statement.
Hibernate performs one or more queries to load the requested entities, when
you provide more primary keys than the default batch size defined in your
database-specific Hibernate dialect or the batch size you defined yourself. The
following code snippet shows an example in which I call the withBatchSize(int
batchSize) method to set the batch size to 2 and Hibernate has to perform two
queries to select the three Book entities.
MultiIdentifierLoadAccess<Book> multi =
session.byMultipleIds(Book.class);
List<Book> books = multi.withBatchSize(2).multiLoad(1L, 2L, 3L);
You can also tell Hibernate not to load any entities that are already stored in
the first-level cache. That behavior is deactivated by default to avoid any
overhead that could slow down your application. If you know that most of the
requested entities are already in the cache, you can activate the additional
check by calling the enableSessionCheck method.
MultiIdentifierLoadAccess<Book> multi =
session.byMultipleIds(Book.class);
List<Book> books = multi.enableSessionCheck(true)
.multiLoad(1L, 2L, 3L);
When the Book entity with id 1 is already stored in the first-level cache,
Hibernate doesn’t add its primary key to the IN clause.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
HibernateMultipleId module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
I show you how to use some of the most popular new data types and concepts
in the following Hibernate tips:
Solution
Hibernate does not support Optional as an attribute type. But you can
implement your own getter method to wrap the attribute in an Optional<T>, if
Hibernate uses field access. This provides you the option to wrap the attribute
which represents the to-one association into an Optional. You can see an
example of it in the following code snippet.
@Entity
public class Book implements Serializable {
...
@ManyToOne
@JoinColumn(name="publisherid")
private Publisher publisher;
...
As you can see in the code snippet, I wrap the publisher into an Optional in the
getPublisher method and return an Optional instead of a Publisher entity. The
Source Code
You can find a project with executable test cases for this Hibernate tip in the
MapOptionalAssociations module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
Hibernate 5 also provides proprietary support for other Java 8 features, like
Streams and the Date and Time API. You can read more about it in:
Solution
The good news is, that since Hibernate 5, you don’t need any additional
annotations to map classes of the Date and Time API. If you’re using an older
Hibernate version you need to implement your own mapping. I show you how
to do that in How to map the Date and Time API with Hibernate 4.4.
Hibernate 5 supports the classes of the Date and Time API as BasicType. In
contrast to the old java.util.Date, the classes of the Date and Time API
provide all information Hibernate needs to map them to the correct JDBC
types. The following table shows to which JDBC types Hibernate maps the new
Java classes.
As you can see in the following code snippet, you don’t need to provide any
146 How to map classes of Java 8’s Date and Time API
additional annotations when you use the Date and Time API classes as entity
attribute types.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
DateAndTime module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Hibernate 5 also provides proprietary support for other Java 8 features, like
Optional and the Stream API. You can read more about it in:
If you can’t use Hibernate 5 or if you’re not allowed to use any proprietary
features, you can implement an AttributeConverter which converts the class
of the Date and Time API to a supported Java type. I show you how to do that
in How to map the Date and Time API with Hibernate 4.4.
Solution
The most obvious but not the most efficient approach is to just call the stream
method on the List interface. You can see an example of it in the following
code snippet. I first call the getResultList method on the Query interface to get
the query result as a List and then call the stream method of the List
interface.
That approach might look OK but it has a drawback that can create
performance issues for huge result sets. In this example, Hibernate gets all the
selected Book entities from the database, stores them in memory, and puts
them into a List. Then I call the stream method and process the results one by
one.
There is no need to fetch all records of the query result at the beginning. For
huge result sets it’s better to scroll through the records and fetch them in
smaller chunks.
You might already know the concept from JDBC result sets or Hibernate’s
ScrollableResults. Scrolling through the records of a result set and processing
them as a Stream are a great fit. Both approaches process one record after the
Since Hibernate 5.2, you can do exactly that with the stream method of
Hibernate’s Query interface. It returns a Stream of the query result and uses
Hibernate’s ScrollableResults internally. That allows you to scroll through the
result set without fetching all records in the beginning.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
ResultsAsStreams module of the example project. If you haven’t already done
so, you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Hibernate 5 also provides proprietary support for other Java 8 features, like
Optional and the Date and Time API. You can read more about it in:
Solution
Hibernate uses two different log categories and log levels to log the executed
SQL statements and their bind parameters:
You can activate and deactivate them independently of each other in your log
configuration.
log4j.rootLogger=info, stdout
# basic log level for all messages
log4j.logger.org.hibernate=info
Hibernate then writes log messages like the following ones to your log file.
The SQL statement in the code snippet isn’t easy to read. That gets a lot better
when you tell Hibernate to format it. You can do that by setting the
configuration parameter hibernate.format_sql to true. You can provide it as a
system property or set it in the persistence.xml file, like in the following code
snippet, or in the hibernate.cfg.xml file.
<properties>
<property name="hibernate.format_sql" value="true" />
...
</properties>
</persistence-unit>
</persistence>
The following code snippet shows the formatted SQL statement which is much
better to read than the previous message.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
LogSQLStatements module of the example project. If you haven’t already done
so, you can download it at http://www.hibernate-tips.com/download-examples.
Solution
The easiest way to count all executed queries is to activate Hibernate’s
statistics component. Hibernate then collects a lot of internal statistics and
provides them as a log message and via the Statistics API.
<persistence>
<persistence-unit name="my-persistence-unit">
<description>Hibernate Tips</description>
<provider>
org.hibernate.jpa.HibernatePersistenceProvider
</provider>
<properties>
<property name="hibernate.generate_statistics"
value="true" />
…
</properties>
</persistence-unit>
</persistence>
Let’s take a look at the log messages first. Hibernate writes a log message,
similar to the following one, at the end of each session. It shows the number of
SQL statements, the time spent for their preparation and execution and the
interaction with the second-level cache.
16:24:55,318 INFO
[org.hibernate.engine.internal.StatisticalLoggingSessionEventListen
er] – Session Metrics {
25659 nanoseconds spent acquiring 1 JDBC connections;
22394 nanoseconds spent releasing 1 JDBC connections;
1091216 nanoseconds spent preparing 12 JDBC statements;
11118842 nanoseconds spent executing 12 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
16999942 nanoseconds spent executing 1 flushes (flushing a total of
17 entities and 17 collections);
63915 nanoseconds spent executing 1 partial-flushes (flushing a
total of 0 entities and 0 collections)
You can also access the Statistics API via Hibernate’s Statistics interface. You
can get it from the SessionFactory. It provides several getter methods that give
you access to more detailed information than the log output.
Learn more
If you want to learn more about Hibernate’s logging features, you should have
a look at these tips:
Solution
Hibernate can add a comment when it generates an SQL statement for a JPQL
or Criteria query or executes a native SQL query. You can see it in your
application log file, when you activate SQL statement logging and in your
database logs.
<persistence>
<persistence-unit name="my-persistence-unit">
<description>Hibernate Tips</description>
<provider>
org.hibernate.jpa.HibernatePersistenceProvider
</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.use_sql_comments"
value="true" />
....
</properties>
</persistence-unit>
</persistence>
I use it in the following example to set the SQL comment for my query to "This
is my comment".
TypedQuery q = em.createQuery(
"SELECT a FROM Author a WHERE a.id = :id",
Author.class);
q.setParameter("id", 1L);
q.setHint("org.hibernate.comment", "This is my comment");
Author a = q.getSingleResult();
Hibernate adds this comment to the generated SQL statement and writes it to
the log file.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CommentSQLStatements module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
Hibernate and JPA support several other hints. I summarized the most
interesting ones in 11 JPA and Hibernate query hints every developer should
know [http://www.thoughts-on-java.org/11-jpa-hibernate-query-hints-every-developer-
know/].
The following Hibernate tips show you several example use cases:
JPQL is comfortable to use but it also has two main disadvantages. The most
noticeable one is that it is not as powerful as SQL. And it also introduces an
additional abstraction on top of SQL and it’s not supported by relational
databases. Hibernate needs to generate an SQL query based on the JPQL query
to interact with the database.
Solution
You can call the createQuery method of the EntityManager with a JPQL query as
a String to create an ad hoc query. The following code snippet shows an
example of such a query. It selects the Book entity with a given id.
TypedQuery<Book> q = em.createQuery(
"SELECT b FROM Book b WHERE b.id = :id", Book.class);
q.setParameter("id", 1L);
Book b = q.getSingleResult();
As you can see, I provide a String with the JPQL query and the class of the Book
entity as parameters to the createQuery method. The JPQL syntax is pretty
similar to SQL. But it uses the entity object model instead of database tables to
define the query. That makes it very comfortable for us Java developers, but
you have to keep in mind that the database still uses SQL. Hibernate, and any
other JPA implementation has to transform the JPQL query into SQL. It is,
therefore, a good practice to activate the logging of the SQL statements during
development to check the generated SQL statements.
The query selects a Book entity with a given id, and I use the named bind
parameter :id as a placeholder in the WHERE clause of the query. Bind
parameters provide huge benefits compared to putting the parameter values
directly into the query String. Hibernate maps the bind parameter value to
the correct type and escapes it if necessary. That makes them easier to handle
and prevents SQL injection vulnerabilities. You can set the value of each bind
parameter by calling the setParameter method of the Query or TypedQuery
interface with the parameter name and its value.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JPQLAdHocQuery module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Ad-hoc queries are just one option to create a JPQL query. You can also use
named queries that you statically define via annotations and reference by
their name. I explain them in more detail in How to create a named JPQL
query.
Solution
Named queries provide a good solution if you want to define a query once and
use it in multiple parts of your business logic. You define a named query with
the @NamedQuery annotation. The following code snippet shows a simple
example of such a query definition.
@Entity
@NamedQuery(
name = Book.QUERY_SELECT_BY_ID,
query = "SELECT b FROM Book b WHERE b.id = :" + Book.PARAM_ID)
public class Book {
...
}
The @NamedQuery annotation requires two parameters: The name and the JPQL
query String. As you can see, I use a static String as the name of the query and
the bind parameter. That makes it easier to reference them in the business
logic.
The JPQL query is the same as you would use in an ad-hoc JPQL query and it
looks very similar to an SQL query. The main difference between SQL and
JPQL is that SQL uses database tables and JPQL the entity model to define the
query. That makes JPQL easier to use for most Java developers, but you still
need to be familiar with SQL. Hibernate transforms the JPQL statement to
That’s all you need to do to define a named JPQL query. You can use its name
to instantiate it in your business logic.
TypedQuery<Book> q = em.createNamedQuery(Book.QUERY_SELECT_BY_ID,
Book.class);
q.setParameter(Book.PARAM_ID, 1L);
Book b = q.getSingleResult();
As you can see in the code snippet, you can use a named JPQL query in a
similar way as an ad-hoc query. The only difference is the way you instantiate
the TypedQuery. You already defined the query with the @NamedQuery
annotation, and you only need to provide its name to the createNamedQuery
method of the EntityManager to instantiate it. That is the point where the usage
of the static String for the query name pays off. It’s a lot easier to use the static
String in your business code, and it allows you to refactor the query name
easily.
I also provided the class of the Book entity as the second parameter to the
createNamedQuery method to get a TypedQuery instance. This one is strongly
typed and doesn’t require any type casting of the query result.
The named query in this example selects a Book entity with a given id, and I
used a named bind parameter in the WHERE clause of the query. I need to set its
value before I can execute the query. I do that by calling the setParameter
method of the TypedQuery method with the parameter name and its value.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JPQLNamedQuery module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Solution
JPQL supports constructor expressions that allow you to define a constructor
call in the SELECT clause of your query. You can see an example of such a query
in the following code snippet.
TypedQuery<BookValue> q = em.createQuery(
"SELECT new org.thoughts.on.java.model.BookValue("
+ "b.id, b.title, b.publisher.name) FROM Book b "
+ "WHERE b.id = :id", BookValue.class);
q.setParameter("id", 1L);
BookValue b = q.getSingleResult();
...
}
Hibernate maps this statement to a SQL query that selects the required
columns from the database and performs the defined constructor call for each
record of the result set.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JPQLConstructorExpression module of the example project. If you haven’t
already done so, you can download it at http://www.hibernate-tips.com/
download-examples.
Solution
You can select scalar values in the same way as you select managed entities.
The following code snippet shows a JPQL query that selects the title of the
Book entity and the name of the associated Publisher entity.
TypedQuery<Object[]> q = em.createQuery(
"SELECT b.title, b.publisher.name FROM Book b WHERE b.id = :id",
Object[].class);
q.setParameter("id", 1L);
Object[] result = q.getSingleResult();
As you can see, you can reference entity attributes instead of entities in the
SELECT clause of your query. Hibernate maps this statement to a SQL query
that only selects the required columns from the database and returns them as
an Object[]. You can see the generated SQL query in the following code
snippet.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JPQLScalarValues module of the example project. If you haven’t already done
so, you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Scalar values are just one of the projections you can use with JPQL. You can
also select managed entities or POJOs:
Solution
Hibernate throws a LazyInitializationException if you try to use the attribute
of a lazily fetched relationship outside of an active Hibernate Session.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
You can avoid that by initializing the relationship before you close the Session.
The easiest way to do that is a JOIN FETCH statement within a query, like the
one in the following code snippet.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author a = em.createQuery(
"SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = 1",
Author.class).getSingleResult();
em.getTransaction().commit();
em.close();
The additional FETCH keyword tells Hibernate not only to join the entity for the
query but also to fetch it from the database to initialize the attribute. That
prevents LazyInitializationExceptions if you access the relationship attribute
outside of an active Hibernate Session.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JoinFetch module of the example project. If you haven’t already done so, you
can download it at http://www.hibernate-tips.com/download-examples.
Solution
JPA 2.1 introduced the TREAT operator to JPQL, which you can use to downcast
an entity within your query.
You can, for example, create a domain model with Authors who have written
different kinds of Publications, like Books and BlogPosts. Publication is the
super class of Book and BlogPost and you can model the relationship between
the Author and the Publication entity.
I use the inheritance strategy SINGLE_TABLE in this example, which maps all
entities of the inheritance hierarchy to the Publication database table. As you
can see in the generated SQL statement, Hibernate joins the records in the
Author table only with records in the Publication that have the value Book in
the DTYPE column. These are the records that represent a Book entity.
Learn more
The TREAT operator is just one of several interesting new features introduced
in JPA 2.1. You can get an overview of the different features and links to more
detailed tutorials in JPA 2.1 – 12 features every developer should know
[http://www.thoughts-on-java.org/jpa-21-overview/].
You can learn more about it in How to map an inheritance hierarchy to one
table and How to map an inheritance hierarchy to multiple tables.
Solution
JPQL supports the following set of database functions that you can use in the
SELECT and WHERE clause of your queries.
Function Description
upper(String s) Transforms String s to upper case
lower(String s) Transforms String s to lower case
current_date() Returns the current date of the
database
current_time() Returns the current time of the
database
current_timestamp() Returns a timestamp of the current
date and time of the database
substring(String s, int offset, int Returns a substring of the given
length) String s
trim(String s) Removes leading and trailing
whitespaces from the given String s
length(String s) Returns the length of the given String
s
locate(String search, String s, int Returns the position of the String
offset) search in s. The search starts at the
position offset
abs(Numeric n) Returns the absolute value of the
given number
The following code snippet shows a query that calls the size function on the
books association.
Query q = em.createQuery(
"SELECT a, size(a.books) FROM Author a GROUP BY a.id");
List<Object[]> results = q.getResultList();
The size function is JPA specific. You can use it to count the elements in a
mapped association. As you can see in the log message, Hibernate generates a
JOIN statement to join the associated table and calls the SQL count function to
count the number of associated records in the book table.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JpqlStandardFunction module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
JPQL supports only a subset of the functions supported by the SQL standard
and no database-specific functions. Since JPA 2.1, you can use the function
function to call functions unsupported by the JPA standard in a CriteriaQuery
How to call a user-defined function in a CriteriaQuery.
Solution
JPA 2.1 added the function function(function_name {, function_arg}) to JPQL
to provide a way to call user-defined and database-specific functions. You
need to provide the name of the function as the first parameter and you can
provide additional parameters that will be used as function arguments.
The following code snippet shows an example that calls the custom database
function calculate. The function returns a Double and I provide the price of
the Book and a query parameter as function arguments.
TypedQuery<Book> q = em.createQuery(
"SELECT b FROM Book b "
+ "WHERE :double2 > function('calculate', b.price, :double1)",
Book.class);
q.setParameter("double1", 10.0D);
q.setParameter("double2", 40.0D);
List<Book> books = q.getResultList();
You can use this approach in the WHERE clause to call all functions supported by
your database.
You can also use the function function in the SELECT clause of
your query. But you then need to register the database
function so that Hibernate knows it’s result type. This makes
the function function superfluous because you can use all
registered functions directly in your query.
Learn More
You can also use the function function in a CriteriaQuery. I show you how in
How to call a user-defined function in a CriteriaQuery.
Solution
With JPA and Hibernate, you have to set the pagination information on the
Query interface and not in the query String as you would do it in SQL. You can
do that by calling the setFirstResult(int startPosition) and
setMaxResults(int maxResults) methods.
The following code snippet shows a simple example that returns the first five
Authors from the database. The result set index is 0 based and you need to
provide 0 as a startPosition to begin with the first element.
To select the next five Authors from the database, you only need to change the
startPosition to 5.
Learn more
You can use the same approach to paginate the result of a CriteriaQuery. I
explain it in more detail in How to use pagination with a CriteriaQuery.
Solution
JPA and Hibernate support the javax.persistence.query.timeout query hint to
define a query timeout in milliseconds. Hibernate uses it to call the setTimeout
method on the JDBC Statement and doesn’t handle the timeout itself. It,
therefore, depends on the JDBC driver and the database capabilities, if the
query hint has any effect.
The following two code snippets show you how to provide the query timeout
hint to a Query and the EntityManager.find method.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
QueryTimeout module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Solution
You can use a JPQL DELETE statement to remove multiple entities at once. It’s
syntax is similar to an SQL DELETE statement. You can see an example of a JPQL
statement that deletes all records from the Book table in the following code
snippet.
You just need to call the createQuery method of the EntityManager with a JPQL
DELETE statement. The JPQL syntax looks very similar to the SQL syntax. It
starts with the keyword DELETE and a reference to the kind of entity you want
to delete. You can also add an optional WHERE clause, like you use in a JPQL
SELECT statement, if you just want to delete a specific set of entities. After you
defined the query, you can call the setParameter method of the Query interface
to set bind parameter values and execute the query by calling the
executeUpdate method. I didn’t use any bind parameters in my DELETE
statement and, therefore, skip the call of the setParameter method.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JPQLDelete module of the example project. If you haven’t already done so, you
can download it at http://www.hibernate-tips.com/download-examples.
Learn More
A JPQL DELETE statement is just one option to delete multiple entities with one
query. You can also use the CriteriaDelete statement as I explain in How to
delete multiple entities with the Criteria API
Solution
You can create a JPQL UPDATE statement with a similar syntax as you know
from SQL. The following code snippet shows an example of such a statement.
It increases the price of all Books by 10%.
As you can see, the JPQL UPDATE statement looks pretty similar to an SQL
UPDATE statement. I first define which entity I want to update and define the
update operation in the SET clause.
This statement updates all Book entities. If you only want to update a specific
set of entities, you can add a WHERE clause to the statement. You can define it in
the same way as you do it for a JPQL SELECT statement.
You can check the generated SQL statement in the log file if you activate
logging for the SQL statements.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
JPQLUpdate module of the example project. If you haven’t already done so, you
can download it at http://www.hibernate-tips.com/download-examples.
Learn More
A JPQL UPDATE statement is just one option to update multiple entities with 1
query. You can create the same statement with the Criteria API as I explain in
How to update multiple entities with the Criteria API.
That is the point when native SQL queries come into play. JPA and Hibernate
are designed as a leaky abstraction and allow you to implement and execute
native SQL queries within your current context. The following Hibernate tips
show you how to do that:
Solution
You can create ad-hoc native queries in a similar way as you create ad-hoc
JPQL queries. You just need to provide a String with the SQL statement to the
createNativeQuery method of the EntityManager.
I do that in the following code snippet with an SQL query that selects a record
from the book table with an id equal to a bind parameter value. The ? you can
see at the end of the query is the placeholder for a positional bind parameter.
You can set the bind parameter values of a native query in the same way as
you set the bind parameters of a JPQL query. You just need to call the
setParameter method of the Query interface with the parameter position and its
value.
The index of positional parameters in ad-hoc native queries
starts at 1.
I also provide the class of the Book entity as an additional parameter to tell
Hibernate to map the query result to a Book entity. I explain the mapping of
native query results to entities in more detail in How to map the result of a
native SQL query to entities.
Learn More
Native queries don’t return the strongly typed results you know from JPQL
queries. They return an Object[] or a List<Object[]> and you need to cast
them yourself or tell Hibernate how to handle the result. Hibernate can map
the query result to entities or POJOs, as I show you in:
Solution
You can define named native SQL queries with the @NamedNativeQuery
annotation. As you can see in the following code snippet, it’s very similar to
the @NamedQuery annotation you use to define a named JPQL query.
@Entity
@NamedNativeQuery(name=Book.QUERY_SELECT_BY_ID,
query="SELECT * FROM book b WHERE id = ?",
resultClass = Book.class)
public class Book {
...
}
You need to provide a name and a native SQL query. As you can see, I use a
static String as the name of the query. That makes it easier to reference it in
the business logic. The SQL query in this example is very simple. It uses a
positional bind parameter to select a record from the book table. Hibernate
also supports named bind parameters for native SQL queries but the JPA
specification does not. So, you should use positional bind parameters, if you
want to be able to use your application with a different JPA implementation.
I also provide a resultClass in this example to tell Hibernate to map the query
result to a Book entity. That’s all you need to do to define a named native query.
Query q = em.createNamedQuery(Book.QUERY_SELECT_BY_ID);
q.setParameter(1, 100);
Book b = (Book) q.getSingleResult();
The index of positional parameters in named native queries
starts at 1.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
NamedNativeQuery module of the example project. If you haven’t already done
so, you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
Native queries don’t return the strongly typed results you know from JPQL
queries. They return an Object[] or a List<Object[]> and you need to cast
them yourself or tell Hibernate how to handle the result. Hibernate can map
the query result to entities or POJOs, as I show you in:
Solution
If your query returns all columns that are mapped to an entity, you can tell
Hibernate to map the result to a managed entity. Afterward, you can use the
entity in the same way as any other managed entity.
1. You can use an implicit mapping if your query result uses the same
column names as your entity mapping.
2. You can create a custom mapping if the column names do not match the
entity mapping.
Implicit Mapping
If you can use it, the implicit mapping is the easier to use and better approach
for most use cases. You only need to provide the class of the entity as the
second parameter to the createNativeQuery method.
If the column names of your query result do not match the column names of
your entity mapping, you have to define the mapping yourself. The following
query shows a very simple example of such a situation. It renames the column
id to bookId so that Hibernate can not use the implicit mapping.
@SqlResultSetMapping(
name = "BookMapping",
entities = @EntityResult(
entityClass = Book.class,
fields = {
@FieldResult(name = "id", column = "bookId"),
@FieldResult(name = "version", column = "version"),
@FieldResult(name = "title", column = "title"),
@FieldResult(name = "publishingDate",
column = "publishingDate"),
@FieldResult(name = "publisher",
column = "publisherid")}))
As you can see in the code snippet, I set a name and a @EntityResult annotation
on the @SqlResultSetMapping annotation. To use the mapping, you need to
provide its name as the second parameter to the createNativeQuery method. So,
make sure to choose a name that describes the mapping and is easy to
remember.
The @EntityResult annotation defines to which entity the query result will be
mapped. Therefore, you need to specify the class of the entity and a set of
@FieldResult annotations. Each @FieldResult annotation defines the mapping
You can then use this mapping by providing its name as the second parameter
to the createNativeQuery method. The defined @SqlResultSetMapping tells
Hibernate to map the query result to a Book entity, but it doesn’t change the
return type of the getSingleResult method. It still returns an Object and you
need to cast it to a Book entity.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
MapNativeQueryToEntity module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
@SqlResultSetMapping is a powerful feature that allows you to define complex
mappings for native query results. You can also use it to map the query result
to a POJO as I show in How to map the result of a native SQL query to a POJO.
Solution
JPA supports @SqlResultSetMappings, which you can use to map the query
result to a POJO. In the following example, I want to map the result of a native
SQL query to BookValue objects.
...
}
Source Code
You can find a project with executable test cases for this Hibernate tip in the
MapNativeQueryToPojo module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
But as you’ll see in the examples, the definition of a query can become
complex. I therefore recommend using JPQL for all use cases that allow the
static definition of a query.
Solution
You can use JPA’s Criteria API to define queries programmatically. It provides
a type-safe way to create a query and supports the same features as JPQL.
Hibernate’s proprietary Criteria API is deprecated. You
should use JPA’s Criteria API instead.
You first need to get a CriteriaBuilder instance from the EntityManager. It’s a
factory class that helps you to define different parts of your query, like bind
parameters, function calls, and predicates.
You then create a CriteriaQuery instance that is the root of the object graph
that represents your query. I recommend providing the return type of your
query as a parameter to the createQuery method. It creates a typed instance of
the CriteriaQuery interface. I want to select Book entities in this example and,
therefore, provide Book.class as the parameter.
In the next step, you define the FROM clause of the query. In this example, I use
the Book entity as the root of the query. Then I call the join method on the Root
interface to join the Book entity with the Author entity. I use the JPA Metamodel
class Book_ to reference the authors attribute of the Book entity. If you’re not
familiar with the JPA Metamodel, take a look at How to reference entity
attributes in a type-safe way, which provides a comfortable and type-safe way
to reference entity attributes.
The first thing you should do is define the bind parameters you want to use in
your query. I create two parameters of type String. One for the first name and
one for the last name. Then I define the WHERE clause by calling the where
method on the CriteriaQuery interface with a Predicate. The Predicate in this
example checks that the firstName and the lastName attribute are equal to the
values of the bind parameters.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);
// execute query
TypedQuery<Book> query = em.createQuery(cq);
query.setParameter(paramFirstName, "Thorben");
query.setParameter(paramLastName, "Janssen");
List<Book> books = query.getResultList();
I’ve defined my query, and now I want to execute it. That requires multiple
steps, as you can see in the code sample.
You first need to call the createQuery method of the EntityManager with your
CriteriaQuery instance. Hibernate returns an instance of the TypedQuery
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CriteriaQuery module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Solution
You can use a similar constructor expression with the Criteria API as you use
in JPQL queries. In the following example, I want to select AuthorValue objects.
...
}
The definition of the CriteriaQuery follows the same approach as you use to
select entities.
You first need to get a CriteriaBuilder instance from the EntityManager. It’s a
factory class that helps you to define different parts of your query, like bind
parameters, function calls, and predicates.
You then create a CriteriaQuery instance that is the root of the object graph
that represents your query. I recommend providing the return type of your
query as a parameter to the createQuery method. It creates a typed instance of
the CriteriaQuery interface. I want to select AuthorValue objects in this
In the next step, you define the FROM clause of the query. In this example, I use
the Author entity as the root of the query.
Then you can define the projection of your query. In this example, it’s a call of
the AuthorValue constructor. The construct method of the CriteriaBuilder
allows you to define constructor calls. Call this method using the class that
Hibernate instantiates as the first parameter and an optional list of Selections
that are used as constructor parameters.
In this example, I use the JPA Metamodel class Author_ to reference the
firstName and lastName attributes of the Author entity. If you’re not familiar
with the JPA Metamodel, take a look at How to reference entity attributes in a
type-safe way, which provides a comfortable and type-safe way to reference
entity attributes.
The definition of the WHERE clause is optional. If you want to select all records
from the database, you can skip this block and execute your query. That’s
what I do in this example. You can see an example of a WHERE clause definition
in How to select entities with a CriteriaQuery.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<AuthorValue> q = cb.createQuery(AuthorValue.class);
Root<Author> root = q.from(Author.class);
q.select(
cb.construct(
AuthorValue.class,
root.get(Author_.firstName),
root.get(Author_.lastName)));
That’s all you need to do to define the CriteriaQuery. You can now execute it in
two steps. You first need to call the createQuery method of the EntityManager
with your CriteriaQuery. This method call returns the same TypedQuery
interface as you use in your JPQL queries. You can use it to set bind parameter
values or to paginate the query result. I don’t use any bind parameters in this
example and therefore, can skip this part. In the final step, you need to call the
getResultList method on the TypedQuery interface to execute the query and
retrieve a List of AuthorValue objects.
When I created the query, I referenced the firstName and lastName attributes
in the constructor call definition. As you can see in the following log messages,
Hibernate used these reference to generates an SQL query that selects the
firstName and lastName columns from the Author table.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CriteriaConstructor module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
If you don’t want to create a POJO to represent your query result, you can also
select a set of scalar values. I show you how to do that in How to select
multiple scalar values in a CriteriaQuery.
And if you want to use the Criteria API in your project, you should also have a
look at the JPA metamodel. It provides a great way to create queries in a type-
safe way. I show you how to use and generate the required classes in How to
reference entity attributes in a type-safe way.
Solution
You can select multiple scalar values using the multiselect method of the
CriteriaQuery interface. The following code snippet shows an example of such
a query.
// Prepare query
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Author> author = q.from(Author.class);
The definition of the CriteriaQuery follows the same approach as you use to
select entities.
You first need to get a CriteriaBuilder instance from the EntityManager. It’s a
factory class that helps you to define different parts of your query, like bind
parameters, function calls, and predicates.
In the next step, you define the FROM clause of the query. In this example, I use
the Author entity as the root of the query.
That’s all you need to do to define the CriteriaQuery. You can now execute the
query in two steps. You first need to call the createQuery method of the
EntityManager with your CriteriaQuery to get an instance of the TypedQuery
interface. This is the same interface as you use for your JPQL queries and you
can use it to set bind parameter values or to paginate the query result. You can
the execute the query by calling the getResultList method on the TypedQuery
interface.
As you can see in the log messages, Hibernate generated a SQL SELECT
statement for the CriteriaQuery. It selects the columns that are mapped by the
firstName and lastName attributes of the Author entity.
Learn more
If you want to select a POJO instead of multiple scalar values, you can define a
constructor call with the construct method of the CriteriaBuilder. I explain it
in more detail in How to select POJOs with a CriteriaQuery.
And if you want to use the Criteria API in your project, you should also have a
look at the JPA metamodel. It provides a great way to create queries in a type-
safe way. I show you how to use and generate the required classes in How to
reference entity attributes in a type-safe way.
Solution
The Criteria API supports a set of database functions you can use in your
queries. You can define function calls with the CriteriaBuilder. It provides a
method for each supported function.
Table 3. Functions
The following code snippet shows a query that calls the size function on the
books association.
The definition of the projection is the important part of this code sample. As
you can see in the code, I select the Author entity and call the size function to
count the number of Books the Author has written. The size function also
requires me to use a GROUP BY clause on the primary key of the Author entity.
The size function is JPA-specific. You can use it to count the elements in a
mapped association. As you can see in the log message, Hibernate generates a
JOIN statement to join the associated table and calls the SQL count function to
count the number of associated records in the BookAuthor table.
Learn More
The Criteria API supports only a subset of the functions supported by the SQL
standard and no database-specific functions. Since JPA 2.1, you can use the
function function to call user-defined or database-specific functions in a
CriteriaQuery How to call a user-defined function in a CriteriaQuery.
Solution
Since JPA 2.1, you can use the function(String name, Class<T> type,
Expression<?>… args) method of the CriteriaBuilder to call user-defined or
database-specific functions. You need to provide the name and the expected
result type of the function as the first two parameters, and you can provide
one or more Expression that will be used as function arguments.
The following code snippet shows an example that calls the user-defined
database function calculate. The definition of the query follows the same
structure as the definition of any other CriteriaQuery. I explain it in more
detail in How to select entities with a CriteriaQuery.
The definition of the WHERE clause is the interesting part of this code sample. I
use it to call the user-defined database function calculate. Before I can do
that, I need to define the two parameters of type Double that I want to provide
to the database function. The calculate function returns a Double, and I
provide the price of the Book and a query parameter as function arguments.
When you execute this CriteriaQuery, Hibernate generates a SQL query with
the defined function call.
You can use this approach in the WHERE clause to call all functions supported by
your database.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CriteriaCustomFunction module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
You can also use the function function in a JPQL query. I show you how in
How to call a user-defined function in a JPQL query.
Solution
The CriteriaUpdate interface allows you to define an update statement that
changes multiple entities. The following code snippet shows an example of
such a statement. It increases the price of all Books by 10%.
Then I define the Root of the query in the same way as I do it for a
CriteriaQuery.
In the next step, I call the set method on the CriteriaUpdate interface to define
the update operation. The first parameter specifies the entity attribute I want
to change. Ìn this example, I want to update the price attribute. The second
parameter is an Expression that calculates the new price. I want to increase
the price by 10% and therefore multiply the current price with 1.1. That’s all
If you don’t want to update all entities, you can add a WHERE clause to the
statement. You can define it in the same way as you do it for a CriteriaQuery.
The only step that’s left is to create a Query based on the CriteriaUpdate and
execute it. Hibernate then generates a SQL UPDATE statement that increases the
price of all Books by 10%. You can check the generated SQL statement in the
log file if you activate logging for the SQL statements.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CriteriaUpdate module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
The CriteriaUpdate interface is just one option to update multiple entities with
one query. You can also use a JPQL update statement as I explain in How to
update multiple entities with one JPQL query.
Solution
You can use the CriteriaDelete interface to remove multiple entities with one
SQL statement. The definition of the statement is similar to the definition of a
CriteriaUpdate or a CriteriaQuery. You can see an example in the following
code snippet. It deletes all Book entities from the database.
You first need to get the CriteriaBuilder from the EntityManager and create a
CriteriaDelete instance. Then you need to define the FROM clause of the delete
statement, and you can add an optional WHERE clause. You do that in the same
way as you do it for a CriteriaQuery.
That’s all you need to do to define a CriteriaDelete statement. You can now
use it to create a Query and execute it.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CriteriaDelete module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
The CriteriaDelete interface is just one option to delete multiple entities with
one query. You can also use a JPQL delete statement as I explain in How to
delete multiple entities with one JPQL query.
Solution
Defining pagination for a CriteriaQuery is easy but not intuitive, if you’re
familiar with SQL. The Criteria API doesn’t provide any method to define
pagination. You have to set the pagination information on the Query and not
on the CriteriaQuery interface. You can do that by calling the
setFirstResult(int startPosition) and setMaxResults(int maxResults)
methods. This provides the advantage that you can also use this approach to
paginate the result of JPQL queries.
The following code snippet shows a simple example that returns the first five
Author entities from the database. The result set index is 0 based and you need
to provide 0 as a startPosition to begin with the first element.
To select the next five Authors from the database, you only need to change the
startPosition to 5.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
CriteriaQueryPagination module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn more
You can use the same approach to paginate the result of a JPQL query. I
explain it in more detail in How to use pagination with JPQL.
Solution
I prefer to use the JPA Metamodel to reference entity attributes in a type-safe
way. The metamodel consists of a generated class for each managed class in
the persistence unit. These classes are stored in the same package and have
the same name as the corresponding managed classes with an added "_" at the
end.
The only thing you have to do to generate these classes is to add a dependency
to Hibernate’s Static Metamodel Generator to your build process like I do in
the following Maven pom.xml file.
<project>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>
...
</dependencies>
...
</project>
Hibernate generates the Metamodel classes with each Maven build and stores
Let’s have a look at an example of a Metamodel class. The first code snippet
shows the Book entity that you know from most examples in this book. The
second one shows the generated metamodel class Book_.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private int version;
@Temporal(TemporalType.DATE)
private Date publishingDate;
@ManyToOne
@JoinColumn(name="publisherid")
private Publisher publisher;
@ManyToMany
@JoinTable(name="BookAuthor",
joinColumns={
@JoinColumn(name="bookId", referencedColumnName="id")},
inverseJoinColumns={
@JoinColumn(name="authorId", referencedColumnName="id")
})
private Set<Author> authors = new HashSet<Author>();
...
}
As you can see, the generated class Book_ has a static attribute for each
attribute of the Book entity. You can use it to reference the entity attribute in a
type-safe way. I do that in the following code snippet to reference the title
and publishingDate attribute of the Book entity in a CriteriaQuery.
// Define CriteriaQuery
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Book> root = cq.from(Book.class);
// Execute Query
List<Tuple> results = em.createQuery(cq).getResultList();
Learn More
The folder of the metamodel classes is not a source folder in most IDEs or a
standard maven project. You can use a maven plugin to automatically register
it as a source folder, as shown in How to automatically add Metamodel classes
to your project.
Solution
Since JPA 2.1, you can use the createStoredProcedureQuery method of the
EntityManager to create an ad-hoc stored procedure call.
If you’re using a Hibernate version older than 4.3 that doesn’t implement JPA
2.1, you need to use a native SQL query to call the stored procedure.
Let’s start with the definition of the stored procedure call. You need to call the
createStoredProcedureQuery method of the EntityManager with the name of the
stored procedure you want to execute. This method returns a
StoredProcedureQuery interface that you can use to define a stored procedure
call, set its input parameters and call it.
In this example, I call the stored procedure calculate. It’s a simple procedure
that expects the input parameters x and y and returns the sum of them. I call
the registerStoredProcedureParameter method of the StoredProcedureQuery for
each of them and register them with their name, type and ParameterMode. The
ParameterMode specifies if the parameter is used as an input (ParameterMode.IN),
output (ParameterMode.OUT), input and output (ParameterMode.INOUT) or as a
result set cursor (ParameterMode.REF_CURSOR).
That’s all you need to do to define the stored procedure call. The second and
third part of an ad-hoc stored procedure call is identical to the execution of a
@NamedStoredProcedureQuery.
I set the values for both input parameters by calling the setParameter method
on the StoredProcedureQuery interface for each of them. And then I call the
execute and the getOutputParameterValue methods to execute the stored
procedure and to read the return value.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
StoredProcedureQuery module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Solution
Before JPA 2.0, you had to use a native SQL query to call a stored procedure. In
version 2.1, JPA introduced an API and the @NamedStoredProcedureQuery
annotation to define stored procedure calls. The annotation-based approach is
the better solution if you have to call the stored procedure from multiple parts
of your business logic without adapting it based on input parameters.
@NamedStoredProcedureQuery(
name = "calculate",
procedureName = "calculate",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN,
type = Double.class,
name = "x"),
@StoredProcedureParameter(mode = ParameterMode.IN,
type = Double.class,
name = "y"),
@StoredProcedureParameter(mode = ParameterMode.OUT,
type = Double.class,
name = "sum")
}
)
That’s all you need to do to define the stored procedure call. You can now use
it in your business code. You just have to provide its name to the
createNamedStoredProcedureQuery method of the EntityManager to instantiate
the query, set the input parameters, execute it, and read the output parameter.
// Instantiated NamedStoredProcedureQuery
StoredProcedureQuery query =
this.em.createNamedStoredProcedureQuery("calculate");
Source Code
You can find a project with executable test cases for this Hibernate tip in the
StoredProcedureQuery module of the example project. If you haven’t already
done so, you can download it at http://www.hibernate-tips.com/download-
examples.
Learn More
A @NamedStoredProcedureQuery is easy to use, but you can’t adapt it at runtime.
If you need more flexibility, you should have a look at the
StoredProcedureQuery API. You can use it to define a stored procedure call at
runtime, which requires more code but also provides more flexibility. I
explain the it in How to create an ad-hoc stored procedure call.
If you like to dive deeper into these topics, please take a look at my Hibernate
Performance Tuning Online Training [http://www.thoughts-on-java.org/course-
hibernate-performance-tuning]. It gets into a lot more details about the general
concept of Hibernate’s different caches and their pitfalls.
Solution
You can use Hibernate’s session-independent second-level cache to cache
entities that are used by multiple Hibernate Session. The cache is deactivated
by default, and you need to set two configuration parameters to activate it:
2. You need to specify the caching provider you want to use by setting the
hibernate.cache.region.factory_class property.
<properties>
...
@Entity
@Cacheable
public class Author {
...
}
You can also use the @Cacheable(false) annotation to exclude an entity from
caching if you use shared_cache_mode DISABLE_SELECTIVE.
That’s all you need to do to activate the second-level cache for your
application. Hibernate now stores all cacheable entities you’ve used in the
second-level cache. When your next use case calls the find method on the
EntityManager or when Hibernate needs to retrieve an entity to initialize an
entity association, it tries to get it from the second-level cache before it
performs a database query.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
2ndLevelCache module of the example project. If you haven’t already done so,
you can download it at http://www.hibernate-tips.com/download-examples.
Learn More
The second-level cache stores entities and no query results. If you want to
cache the result of a query, you can use Hibernate’s Query Cache. I show you
how in How to use the query cache to avoid additional queries.
Solution
Hibernate also supports the query cache, which stores query results.
If you identify a query that you often execute with the same parameter values,
you need to activate the query cache in the persistence.xml file. You do that by
setting the parameter hibernate.cache.use_query_cache to true and defining a
hibernate.cache.region.factory_class.
<persistence>
<persistence-unit name="my-persistence-unit">
...
<properties>
...
<!-- configure caching -->
<property name="hibernate.cache.use_query_cache"
value="true"/>
<property name="hibernate.cache.region.factory_class"
value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
</properties>
</persistence-unit>
</persistence>
Hibernate now stores the result of this query in the query cache. When your
next use case executes this query, Hibernate checks if the query cache
contains a result for the given parameter values. If the cache contains a
matching record, Hibernate gets the query result from there. Otherwise, it
executes the query and store its result in the cache.
Source Code
You can find a project with executable test cases for this Hibernate tip in the
QueryCache module of the example project. If you haven’t already done so, you
can download it at http://www.hibernate-tips.com/download-examples.
Learn More
The query cache stores query results and no entities. If you want to cache
entities that you often use in your business code, you can use the second-level
cache. I show you how in How to store an entity in the second-level cache.
Thank you to Steve Ebersole, who immediately agreed to write the foreword
and provided great feedback on the book.
I would also like to thank Michael Simons for helping me with the How to
bootstrap Hibernate with Spring Boot tip.
I also want to thank Nermina Miller, who edited this book and fixed all my
spelling, grammar, and punctuation mistakes.
Thank you to Dan Allen, who helped me with Asciidoctor. Without your
incredible support, I wouldn’t have been able to finish the book in time.
Thank you to Tom Oberbichler, who guided me through the creation process
of this book and helped me to focus on the right tasks at the right time. Your
support gave me the peace of mind that I had the most important things under
control even when it felt like I had to work on all of them at once.
And, a special thank you goes to my amazing wife Sandra, who encouraged
me in so many ways over the years. She’s always there and supports me in
any way she can, even as I spend nights and weekends writing books or
recording online courses. Without you, I would have never been able to do it
all.
Thanks also to my little son, Lars, who reminds me that there are so many
other fantastic things in life.
I love you both!
Thorben
Frits Walraven
Frits has been a software engineer for about 15 years. He got his MSc in
Computer Science from the Technical University in Twente, Netherlands, back
in 1996. After a few years of coding in C, he jumped into the big Java world.
Nowadays he is mostly involved with functional design but tries to keep his
coding alive through Oracle Certification. He currently holds the SCJP5,
SCWCD5, OCEEJB6, and OCEWSD6 certificates and enjoys writing and sharing
his notes. He is the author of the Enthuware Java Web Services Developer EE6
mock exams [http://enthuware.com/index.php/mock-exams/oracle-certified-expert/oce-
web-services-mock-questions].
Mark Spritzler
Mark Spritzler is a former JBoss and SpringSource staff member, and has been
teaching enterprise software for more than 25 years as a software developer.
He has spent the last 13 years writing Hibernate/JPA applications on many
enterprise applications.
Petri Kainulainen
Sandra Janssen
248 Reviewers
Thorben Janssen
Thorben Janssen has been a software developer and architect for more than
15 years. More than a decade ago, he used one of the first Hibernate releases
to implement the persistence layer of enterprise applications. Since then, he
has used the framework to implement applications of various sizes with
complex business and performance requirements.
After blogging for several years about JPA and Hibernate, he decided to quit
his day job in October 2016 to follow his passion for writing and teaching.
Since then, he has been working as an independent trainer, author, and
consultant to show software developers how to use Hibernate and JPA to
avoid common problems and implement their persistence layer with ease.