Hibernate Best Practices
Hibernate Best Practices
1.1 Entities
You should use the entity projection when you need all attributes of
the entity and for update or delete operations that affect only a small
number of entities.
em.find(Author.class, 1L);
1.2 POJOs
POJO projections allow you to create use case specific
representations of the database record. This is especially useful if you
only need a small subset of the entity attributes or if you need
attributes from several related entities.
www.thoughts-on-java.org
Hibernate Best Practices
2. Use the kind of query that fits your use case
JPA and Hibernate offer multiple implicit and explicit options to
define a query. None of them is a good fit for every use case, and you
should, therefore, make sure to select the one that fits best.
2.1 EntityManager.find()
The EntityManager.find() method is the best and easiest way to get
an entity by its primary key.
em.find(Author.class, 1L);
2.2 JPQL
JPQL is very similar to SQL, but it operates on entities and their
relationships instead of database tables. You can use it to create
queries of low and moderate complexity.
TypedQuery<Author> q = em.createQuery(
SELECT a FROM Author a JOIN a.books b
+ WHERE b.title = :title,
Author.class);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Author> q = cb.createQuery(Author.class);
Root<Author> author = q.from(Author.class);
q.select(author);
www.thoughts-on-java.org
Hibernate Best Practices
2.4 Native Queries
Native queries provide you the chance to write and execute plain
SQL statements.
www.thoughts-on-java.org
Hibernate Best Practices
4. Use static Strings for named queries and parameter
names
This is just a small thing but its much easier to work with named
queries and their parameters if you define their names as static
Strings. I prefer to define them as attributes of the entities with
which you can use them but you can also create a class that holds all
query and parameter names.
@NamedQuery(
name = Author.QUERY_FIND_BY_LAST_NAME,
query = SELECT a FROM Author a
+ WHERE a.lastName = :
+ Author.PARAM_LAST_NAME)
@Entity
public class Author {
www.thoughts-on-java.org
Hibernate Best Practices
5. Use JPA Metamodel when working with Criteria API
The JPA Metamodel generates an additional class for each entity. You
can use it to reference entity attributes in a type safe way when
creating a Criteria query.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Author> q = cb.createQuery(Author.class);
Root<Author> author = q.from(Author.class);
q.select(author);
q.where(cb.equal(author.get(Author_.lastName), lastName));
I explain the JPA Metamodel and how you can generate its classes in
Create type-safe queries with the JPA static metamodel.
@Id
@GeneratedValue
@Column(name = id, updatable = false, nullable = false)
private Long id;
www.thoughts-on-java.org
Hibernate Best Practices
7. Specify natural identifier
You should specify natural identifiers, even if you decide to use a
surrogate key as your primary key. A natural identifier nevertheless
identifies a database record and an object in the real world. A lot of
use cases use them instead of an artificial, surrogate key.
It is, therefore, good practice to model them as unique keys in your
database. Hibernate also allows you to model them as a natural
identifier of an entity and provides an extra API for retrieving them
from the database.
The only thing you have to do to model an attribute is a natural id, is
to annotate it with @NaturalId.
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = id, updatable = false, nullable = false)
private Long id;
@NaturalId
private String isbn;
www.thoughts-on-java.org
Hibernate Best Practices
8. Use SQL scripts to create the database schema
The database schema has a huge influence on the performance and
size of your database. You, therefore, should design and optimize the
database schema yourself and export it as an SQL script.
The following snippet shows a persistence.xml file which tells
Hibernate to run the create.sql script to setup the database.
<persistence>
<persistence-unit name=my-persistence-unit transaction-
type=JTA>
<properties>
<property name=
javax.persistence.schema-generation.scripts.action
value=create/>
<property name=
javax.persistence.schema-generation.scripts.create-
target
value=./create.sql/>
</properties>
</persistence-unit>
</persistence>
You can learn more about the different configuration parameters in
Standardized schema generation and data loading with JPA 2.1.
www.thoughts-on-java.org
Hibernate Best Practices
9. Log and analyze all queries during development
Hibernate hides all database interactions behind its API, and its often
difficult to guess how many queries it will perform for a given use
case. The best way to handle this issue is to log all SQL statements
during development and analyze them before you finish your
implementation task. You can do that by setting the log level of the
org.hibernate.SQL category to DEBUG.
I explain Hibernates most important log categories and provide
detailed recommendations for a development and a production
configuration in my Hibernate Logging Guide.
@ManyToMany(mappedBy = authors,
fetch = FetchType.LAZY)
private Set<Book> books = new HashSet<Book>();
www.thoughts-on-java.org
Hibernate Best Practices
11. Initialize required lazy relationships with the initial query
FetchType.LAZY tells Hibernate to fetch the related entities only
when theyre used. This helps you to avoid certain performance
issues. But its also the reason for the LazyInitializationException and
the n+1 select issue which occurs when Hibernate has to perform an
additional query to initialize a relationship for each of the selected n
entities.
The best way to avoid both issues is to fetch an entity together with
the relationships you need for your use case. One option to do that is
to use a JPQL query with a JOIN FETCH statement.
www.thoughts-on-java.org
Hibernate Best Practices
13. Use @Immutable when possible
Hibernate regularly performs dirty checks on all entities that are
associated with the current PersistenceContext to detect required
database updates. This is a great thing for all mutable entities. But
not all entities have to be mutable.
Entities can also map read-only database views or tables. Performing
any dirty checks on these entities is an overhead that you should
avoid.
You can do this by annotating the entity with @Immutable. Hibernate
will then ignore it in all dirty checks and will not write any changes to
the database.
@Entity
@Immutable
public class BookView {
www.thoughts-on-java.org