0% found this document useful (0 votes)
3 views

Tutoriales JPA

The document discusses the use of JPA and Hibernate for database management, highlighting their suitability for relational databases and the limitations with NoSQL databases. It provides guidance on configuring JPA through the persistence.xml file, including essential elements and optional configurations. Additionally, it covers key annotations used in JPA for defining entity classes and their mappings to database tables, as well as managing relationships between entities.

Uploaded by

jorgefkluka
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Tutoriales JPA

The document discusses the use of JPA and Hibernate for database management, highlighting their suitability for relational databases and the limitations with NoSQL databases. It provides guidance on configuring JPA through the persistence.xml file, including essential elements and optional configurations. Additionally, it covers key annotations used in JPA for defining entity classes and their mappings to database tables, as well as managing relationships between entities.

Uploaded by

jorgefkluka
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 103

Should you use JPA for your next project?

What kind of database do you use?


NoSQL Relational database

Do you use a static or dynamic/configurable


domain model?
dynamic static

What is the main focus of your use


cases?
Complex A little bit of Standard
reporting both CRUD
/ data
mining

How many highly complex


queries, stored procedures,
and custom database
functions will you have?
No, the A lot A few Not that
mapping many
define-
tion is
No, JPA pretty No, it’s No, JPA Yes, JPA
and static easier to and and Yes, this
Hibernate and imple- Hibernate Hibernate is a great
are not a can’t be ment the are not a can be a applica-
good fit easily required good fit good fit tion for
for adapted complex for this for this JPA and
NoSQL at queries applica- applica- Hiber-
databases. runtime. with SQL. tion. tion. nate.

www.thoughts-on-java.org
A Beginner's Guide to JPA's persistence.xml
The most basic persistence.xml configuration
You can use JPA with a very short, basic configuration. You only need
a persistence element as the root element and a persistence-unit
element with a name attribute. The attribute is used to identify the
persistence unit, and you can use it during the bootstrapping process
to instantiate a specific EntityManagerFactory.

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" version="2.2"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persist
ence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
>
<!-- Define persistence unit -->
<persistence-unit name="my-persistence-unit">
</persistence-unit>
</persistence>

When you use this configuration, you configure a persistence unit


with the name "my-persistence-unit" without defining a dependency
to a specific JPA implementation. You also rely on a list of defaults
defined by the specification:
 Your persistence provider scans the root of your persistence
unit and adds all annotated managed persistence classes to the
persistence unit.
 If your META-INF directory contains a file called orm.xml, it
gets treated as a mapping file and all included mapping
information get used.
 The used transaction type depends on the environment in
which you deploy your application. In a Jakarta EE environment,
JPA expects that the container provides a JTA-compliant

www.thoughts-on-java.org
A Beginner's Guide to JPA's persistence.xml
connection provider. In a Java SE environment, it uses a
RESOURCE_LOCAL transaction instead.
 You don't configure any database connection. JPA, therefore,
expects that you provide a datasource at runtime.
 All JPA implementations support a set of proprietary
configuration parameters. Examples for that are the logging
configuration in EclipseLink JPA or Hibernate's database dialect.
As you don't define any of them in this basic configuration, you
also rely on all provider-specific defaults.

Optional configuration elements you should know


You can use the following XML elements to customize the basic
configuration:
<description>This is a short Provide a short description of
text describing my persistence your persistence unit.
unit.</description>
<class>org.thoughts.on.java.jpa.By adding one or more class
beginners.Professor</class> elements to your persistence-unit
configuration, you can add
classes to your persistence unit
which are not in the root of the
persistence unit.
<jar-file>my-entities.jar</jar- You can use one or more jar-file
file> elements to add all managed
classes contained in these jar
files.
<exclude-unlisted- The exclude-unlisted-classes
classes>true</exclude- element enables you to exclude
unlisted-classes> all classes from the persistence-
unit which were not explicitly
included.
<mapping- You can use external, XML-based
file>file:\\\C:\dev\wrk\XmlM mapping files. By default, your
apping\XmlMappings\myMap persistence provider checks if the
pings.xml</mapping-file> META-INF directory contains a
file called orm.xml and includes
its mapping information.
If you want to use multiple

www.thoughts-on-java.org
A Beginner's Guide to JPA's persistence.xml
mapping files or if the name of
your file doesn't match the
default naming pattern, you can
reference them in one or more
mapping-file elements.
<provider>org.eclipse.persisten If you use any proprietary
ce.jpa.PersistenceProvider</pr features of your persistence
ovider> provider, you should specify a
dependency to it.
<jta-data- These elements are mostly used
source>java:app/jdbc/MyData in Jakarta EE environments. They
Source</jta-data-source> enable you to reference the JNDI
<non-jta-data- name of a datasource that is or is
source>java:app/jdbc/MyData not compliant with the Java
Source</non-jta-data-source> Transaction API.
<shared-cache- You can activate the 2nd level
mode>ENABLE_SELECTIVE</s cache and specify its mode with
hared-cache-mode> the shared-cache-mode element.
You can choose between 4
different options:
1. ALL - To cache all entities
2. NONE - To cache none of
your entities (default)
3. ENABLE_SELECTIVE - To
cache only the entities
annotated with @Cacheable
or @Cacheable(true)
4. DISABLE_SELECTIVE - To
cache all entities not
annotated with
@Cacheable(false)
<validation- The 3 different values supported
mode>CALLBACK</validation- by the validation-mode element
mode> enable you to activate or
deactivate the validation:
1. AUTO - Perform the
validation if a bean
validation implementation is
available (default)
2. CALLBACK- Activate the
validation and throw an

www.thoughts-on-java.org
A Beginner's Guide to JPA's persistence.xml
exception if no bean
validation implementation is
available
3. NONE - Do not perform any
validation
<properties> You can use the properties
<property name = javax.persistence.lock.timeout
"javax.persistence.lock.timeout" and
value="100"/> javax.persistence.query.timeout
<property name = to define the pessimistic lock
"javax.persistence.query.timeou timeout and the query timeout in
t" value="100"/> milliseconds.
</properties>
<properties> You can configure one or more
<property name = groups that get validated for each
"javax.persistence.validation.gr lifecycle state change by using
oup.pre-persist" value = these property.
"javax.validation.groups.MyPers
istValidation"/>
<property name =
"javax.persistence.validation.gr
oup.pre-update" value =
"javax.validation.groups.MyUpd
ateValidation"/>
<property name =
"javax.persistence.validation.gr
oup.pre-remove" value =
"javax.validation.groups.MyRem
ovetValidation"/>
</properties>
<properties> In a Java SE environment, you
<property name = might not have a datasource that
"javax.persistence.jdbc.driver" you can reference to define the
value = "org.postgresql.Driver" database connection. In these
/> situations, you can use this set of
<property name = properties to specify the JDBC
"javax.persistence.jdbc.url" driver class, the connection URL
value = and the login information that
"jdbc:postgresql://localhost:54 your persistence provider shall
32/jpaForBeginners" /> use to connect to the database.

www.thoughts-on-java.org
A Beginner's Guide to JPA's persistence.xml
<property name =
"javax.persistence.jdbc.user"
value="postgres" />
<property name =
"javax.persistence.jdbc.passwor
d" value = "postgres" />
</properties>
<properties> Since version 2.1, JPA can create a
<property name = new database at startup and
"javax.persistence.schema- initialize it with a predefined
generation.database.action" dataset. You can activate and
value = "drop-and-create" /> configure this feature by adding
<property name = these properties to your
"javax.persistence.schema- configuration.
generation.create-script-
source" value = "create-db.sql"
/>
<property name =
"javax.persistence.schema-
generation.drop-script-source"
value = "drop-db.sql" />
<property name =
"javax.persistence.sql-load-
script-source" value = "data.sql"
/>
</properties>
<properties> You can tell your persistence
<property name = provider to generate your
"javax.persistence.schema- database scripts by configuring
generation.scripts.action" value these properties.
= "drop-and-create"/>
<property name = Please be aware that these scripts
"javax.persistence.schema- often need to be adapted and
generation.scripts.create- optimized before you can use
target" value = "./create.sql"/> them in production.
<property name =
"javax.persistence.schema-
generation.scripts.drop-target"
value = "./drop.sql"/>
</properties>

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
When you start learning and using Hibernate and JPA, the number of
annotations might be overwhelming. But as long as you rely on the
defaults, you can implement your persistence layer using only a small
subset of them.

Let’s take a look at the most important annotations and their


attributes. For each annotation, I will explain which attributes you
really need and which ones you should better avoid.

And if you want to dive deeper into JPA and make sure you have a
solid understanding of all the basic concepts, I recommend enrolling
in my JPA for Beginners online course.

Define an Entity Class


JPA entities don’t need to implement any interface or extend a
superclass. They are simple POJOs. But you still need to identify a
class as an entity class, and you might want to adapt the default table
mapping.

@Entity
The JPA specification requires the @Entity annotation. It identifies a
class as an entity class.

@Entity
public class Author { ... }

Attributes

name Unique name of the entity by which it gets referenced in


JPQL queries

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
@Table
By default, each entity class maps a database table with the same
name in the default schema of your database. You can customize this
mapping using the name, schema, and catalog attributes of the @Table
annotation.

@Entity
@Table(name = "AUTHORS", schema = "STORE")
public class Author {

Attributes

name Name of the database table.

Schema Name of the database schema in which the table


is located.

catalog Name of the database catalog that stores the


metadata information of the table.

indexes Index definitions used in CREATE TABLE


statement.

Not recommended to be used.

uniqueConstraints Unique constraints used in CREATE TABLE


statement.

Not recommended to be used.

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
Basic Column Mappings
By default, all JPA implementations map each entity attribute to a
database column with the same name and a compatible type. The
following annotations enable you to perform basic customizations of
these mappings. You can, for example, change the name of the
column, adapt the type mapping, identify primary key attributes, and
generate unique values for them.

@Column
Let’s start with the @Column annotation. It is an optional annotation
that enables you to customize the mapping between the entity
attribute and the database column.

@Entity
public class Book {

@Column(name = "title", updatable = false, insertable = true)


private String title;

...
}

Attributes

name Name of the database column.

insertable Set to false to exclude from SQL INSERT


statements.

updatable Set to false to exclude from SQL UPDATE


statements.

table Only used when you map your entity to 2 database


tables.

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
Specifies the table that contains the mapped
column.

columnDefinition SQL fragment that’s used during table definition.

Not recommended to be used.

length Length of a String-valued database column

Not recommended to be used.

scale Scale of a decimal column.

Not recommended to be used.

precision Precision of a decimal column.

Not recommended to be used.

unique Defines a unique constraint on the mapped


column.

Not recommended to be used.

@Id
JPA and Hibernate require you to specify at least one primary key
attribute for each entity. You can do that by annotating an attribute
with the @Id annotation.

@Entity
public class Author {

@Id
private Long id;

...
}

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
@GeneratedValue
When we’re talking about primary keys, we also need to talk about
sequences and auto-incremented database columns. These are the 2
most common database features to generate unique primary key
values.

If you annotate your primary key attribute with the @GeneratedValue


annotation, you can use a database sequence by setting the strategy
attribute to GenerationType.SEQUENCE. Or, if you want to use an
auto-incremented database column to generate your primary key
values, you need to set the strategy to GenerationType.IDENTITY.

@Entity
public class Author {

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

...
}

Attributes

strategy The strategy used to generate the primary key value:

 GenerationType.SEQUENCE

 GenerationType.IDENTITY

generator References a custom generator configuration

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
@Enumerated
The @Enumerated annotation enables you to define how anenum
attribute gets persisted in the database. By default, all JPA
implementations map the ordinal value of the enum to a numeric
database column.

As I explained in more detail in my guide on enum mappings, the


ordinal makes it hard to add or remove values to the enum. The
mapping as a String is more robust and much easier to read. You can
activate this mapping by EnumType.STRING to the @Enumerated
annotation.

@Entity
public class Author {

@Enumerated(EnumType.STRING)
private AuthorStatus status;

...
}

Attributes

value The mapping strategy used for the enum:

 EnumType.STRING

 EnumType.ORDINAL

@Temporal
If you’re still using java.util.Date or java.util.Calendar as your
attribute types, you need to annotate the attribute with @Temporal.
Using this annotation, you can define if the attribute shall be mapped
as an SQL DATE, TIME, or TIMESTAMP.

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
@Entity
public class Author {

@Temporal(TemporalType.DATE)
private Date dateOfBirth;

...
}

Attributes

value The SQL type to which the attribute shall be mapped:

 TemporalType.DATE

 TemporalType.TIME

 TemporalType.TIMESTAMP

@Lob
Using JPA’s @Lobannotation, you can map a BLOB (binary large
object) to a byte[] and a CLOB (character large object) to a String.
Your persistence provider then fetches the whole BLOB or CLOB
when it initializes the entity attribute.

@Entity
public class Book {

@Lob
private byte[] cover;

...
}

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
In addition to that, Hibernate also supports mappings to java.sql.Blob
and java.sql.Clob. These are not as easy to use a byte[] or a String, but
they can provide better performance. I explained that mapping in
great detail in Mapping BLOBs and CLOBs with Hibernate and JPA.

Association Mappings
You can also map associations between your entities. In the table
model, these are modeled as foreign key columns. These associations
are mapped as attributes of the type of the associated entity or
a Collection of associated entities, in your domain model.

In both cases, you need to describe the association mapping. You can
do that using a @ManyToMany,@ManyToOne, @OneToMany, or
@OneToOne annotation.

@ManyToMany
Many-to-many associations are very common in relational table
models. In your domain model, you can map this association in a uni-
or bidirectional way using attributes of type List,Set or Map, and a
@ManyToMany annotations.

@Entity
@Table(name = "BOOKS")
public class Book {

@ManyToMany
private Set<Author> authors;

...
}

Attributes

fetch Shall the association get fetched eagerly or lazily.

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
cascade The operations that shall be cascaded to the associated
entities.

If you want to model the association in a bidirectional way, you need


to implement a similar mapping on the referenced entity. But this
time, you also need to set the mappedBy attribute of the
@ManyToMany annotation to the name of the attribute that owns the
association. To your persistence provider, this identifies the mapping
as a bidirectional one.
@Entity
public class Author {

@ManyToMany(mappedBy = "authors")
private Set<Book> books;

...
}

You use the same @ManyToMany annotation to define the


referencing side of the association, as you use to specify the owning
side of it. So, you can use the same cascade and fetch attributes, as I
described before.

@ManyToOne and @OneToMany


Many-to-one and one-to-many associations represent the same
association from 2 different perspectives. So, it’s no surprise that you
can use them together to define a bidirectional association. You can
also use each of them on their own to create a unidirectional many-
to-one or one-to-many association. But you should avoid
unidirectional one-to-many associations. Hibernate handles them
very inefficient.

@ManyToOne
Let’s take a closer look at the @ManyToOne annotation. It defines the
owning side of a bidirectional many-to-one/one-to-many

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
association. You do that on the entity that maps the database table
that contains the foreign key column.

@Entity
public class Book {

@ManyToOne(fetch = FetchType.LAZY)
private Publisher publisher;

...
}

Attributes

fetch Shall the association get fetched eagerly or lazily.

cascade The operations that shall be cascaded to the associated


entities.

optional Indicates if this association is mandatory

@OneToMany
Similar to the referencing side of a bidirectional many-to-many
association, you can reference the name of the attribute that owns
the association in the mappedBy attribute. That tells your persistence
provider that this is the referencing side of a bidirectional
association, and it reuses the association mapping defined by the
owning side.

@Entity
public class Publisher {

@OneToMany(mappedBy = "publisher", cascade = CascadeType.ALL)


private Set<Book> books;

...
}

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
Attributes

fetch Shall the association get fetched eagerly or lazily.

cascade The operations that shall be cascaded to the


associated entities.

orphanRemoval Shall child entities get removed when removed from


association

@OneToOne
One-to-one associations are only rarely used in relational table
models. You can map them using a@OneToOne annotation.

Similar to the previously discussed association mapping, you can


model a uni- or bidirectional one-to-one associations. The attribute
that’s defined on the entity that maps the database table that
contains the foreign key column owns the association.

@Entity
public class Manuscript {

@OneToOne(fetch = FetchType.LAZY)
private Book book;

...
}

Attributes

fetch Shall the association get fetched eagerly or lazily.

cascade The operations that shall be cascaded to the associated


entities.

www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
optional Indicates if this association is mandatory

And if you model it as a bidirectional association, you need to set the


mappedBy attribute of the referencing side of the association to the
attribute name that owns the association.

@Entity
public class Book {

@OneToOne(mappedBy = "book")
private Manuscript manuscript;

...
}

www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
The access strategy defines how your JPA implementation, e.g.,
Hibernate or EclipseLink, access your entity attributes. You can
choose between field-based access and property-based access:
 If you use field-based access, your JPA implementation uses
reflection to read or write your entity attributes directly. It also
expects that you place your mapping annotations on your entity
attributes.
 If you use property-based access, you need to annotate the
getter methods of your entity attributes with the required
mapping annotations. Your JPA implementation then calls the
getter and setter methods to access your entity attributes.

How to specify the access strategy


Default configuration
By default, you specify the access strategy implicitly by annotating
your primary key attribute or its getter method with an @Id
annotation. If you annotate the attribute itself, Hibernate uses field-
based access.

@Entity
public class Review {

@Id
protected Long id;


}

And if you annotate a getter method with the @Id annotation,


Hibernate uses property-based access to set and read the attributes
of this entity.

www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
@Entity
public class Review {

@Id
public Long getId() {
return id;
}


}

Override the default access strategy


If you want to mix both access strategies within one entity or a
hierarchy of entities, you need to override the default strategy with
an @Access annotation. Otherwise, the JPA specification defines the
behavior as undefined.
@Entity
public class Review {

@Version
@Access(AccessType.FIELD)
private int version;

@Id
public Long getId() {
return id;
}

}

www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
5 reasons why you should use field-based access
As explained earlier, you most often specify your access strategy by
annotating the attributes or getter methods of your entity. But which
strategy should you choose? Does it make a real difference?
Yes, it does make a difference. I always recommend using field-based
access. Here are 5 reasons why field-based access is the better
access strategy.

Reason 1: Better readability of your code


If you use field-based access, you annotate your entity attributes
with your mapping annotations. By placing the definition of all entity
attributes at the top of your class, you get a relatively compact view
of all attributes and their mappings.

Reason 2: Omit getter or setter methods that shouldn’t be called by your


application
Another advantage of field-based access is that your persistence
provider, e.g., Hibernate or EclipseLink, doesn’t use the getter and
setter methods of your entity attributes. That means that you don’t
need to provide any method that shouldn’t be used by your business
code. This is most often the case for setter methods of generated
primary key attributes or version columns. Your persistence provider
manages the values of these attributes, and you should not set them
programmatically.

You can also provide utility methods that enable you to add or
remove elements from a to-many association and prevent direct
access to the underlying List or Set. This makes the implementation
of your business code more comfortable and is a general best
practice for bi-directional many-to-many associations.

www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
@Entity
public class Book {

@ManyToMany
Set<Author> authors;

public void addAuthor(Author a) {


this.authors.add(a);
a.getBooks.add(this);
}

public void removeAuthor(Author a) {


this.authors.remove(a);
a.getBooks().remove(this);
}


}

Reason 3: Flexible implementation of getter and setter methods


Because your persistence provider doesn’t call the getter and setter
methods, they are not forced to fulfill any external requirements. You
can implement these methods in any way you want. That enables you
to implement business-specific validation rules, to trigger additional
business logic or to convert the entity attribute into a different data
type.
I use that in the following example to wrap an optional association in
a Java Optional. Even so, JPA and Hibernate don’t support Optional as
an attribute type, field-based access enables you to use it as the
return type of a getter method.

www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
@Entity
public class Book {

@ManyToOne
Publisher publisher;

public void setPublisher(Publisher p) {


this.publisher = p;
}

public Optional<Publisher> getPublisher() {


return Optional<Publisher>.ofNullable(this.publisher);
}


}

Reason 4: No need to mark utility methods as @Transient


Another benefit of the field-based access strategy is that you don’t
need to annotate your utility methods with @Transient. This
annotation tells your persistence provider that a method or attribute
is not part of the entity persistent state. And because with field-type
access the persistent state gets defined by the attributes of your
entity, your JPA implementation ignores all methods of your entity.

Reason 5: Avoid bugs when working with proxies


Hibernate uses proxies for lazily fetched to-one associations so that
it can control the initialization of these associations. That approach
works fine in almost all situations. But it introduces a dangerous
pitfall if you use property-based access.
If you use property-based access, Hibernate initializes the attributes
of the proxy object when you call the getter method. That’s always

www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
the case if you use the proxy object in your business code. But quite a
lot of equals and hashCode implementations access the attributes
directly. If this is the first time you access any of the proxy attributes,
these attributes are still uninitialized.
@Entity
public class Book {

@NaturalId
String isbn;

...

@Override
public int hashCode() {
return Objects.hashCode(isbn);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Book other = (Book) obj;
return Objects.equals(isbn, other.isbn);
}
}

You can easily avoid this pitfall by using the field-based access
strategy.

www.thoughts-on-java.org
How to generate primary keys
4 options to generate primary keys
The JPA specification supports 4 different primary key generation
strategies which generate the primary key values programmatically
or use database features, like auto-incremented columns or
sequences.

GenerationType.AUTO
The GenerationType.AUTO is the default generation type and lets the
persistence provider choose the generation strategy.

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;

GenerationType.IDENTITY
The GenerationType.IDENTITY is easiest to use but not the best one
from a performance point of view. It relies on an auto-incremented
database column and lets the database generate a new value with
each insert operation.
It requires Hibernate to perform the insert statement immediately
and prevents it from using other performance optimization
techniques.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;

www.thoughts-on-java.org
How to generate primary keys
GenerationType.SEQUENCE
The GenerationType.SEQUENCE is my preferred way to generate
primary key values and uses a database sequence to generate unique
values.
You can use the @SequenceGenerator annotation to define the name
and schema of the database sequence, the allocation size and the
incremental size of the sequence.

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "book_generator")
@SequenceGenerator(name="book_generator",
sequenceName = "book_seq",
initialValue=100, allocationSize=50)
@Column(name = "id", updatable = false, nullable = false)
private Long id;

www.thoughts-on-java.org
How to generate primary keys
GenerationType.TABLE
The GenerationType.TABLE simulates a sequence by storing and
updating its current value in a database table which requires the use
of pessimistic.
You should use GenerationType.SEQUENCE, if it’s supported by your
database.
The @TableGenerator annotation is similar to the
@SequenceGenerator annotation and you can use it to define
database table Hibernate shall use for the generator.

@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "book_generator")
@TableGenerator(name="book_generator",
table="id_generator",
schema="bookstore")
@Column(name = "id", updatable = false, nullable = false)
private Long id;

www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
With JPA and Hibernate you can do much more than just mapping a
simple, numeric primary key. You can:
 choose between different strategies to generate unique primary
key values,
 use UUIDs and generate their values,
 map composite primary keys,
 share primary key values across associations and
 map natural IDs.

Generate Numeric Primary Key Values


You can either set primary key values programmatically or use one of
JPA’s generation strategies to create them automatically. The easiest
way to do that is to annotate your primary key attribute with a
@GeneratedValue annotation. Hibernate will then pick a strategy
based on the database-specific dialect.
But using the auto strategy, or not referencing a strategy at all, is the
simplest but not the best way. It’s better to specify the strategy. You
can choose between:
 GenerationType.AUTO – Let Hibernate pick one of the
following strategies.
 GenerationType.SEQUENCE – Use a database sequence.
 GenerationType.IDENTITY – Use an autoincremented database
columns.
 GenerationType.TABLE – Use a database table to simulate a
sequence.
That ensures that a Hibernate update will not accidentally change
your generation strategy and if you’re using the
GenerationType.SEQUENCE, it will also activate Hibernate’s
performance optimizations.
Defining the strategy is simple. You just need to provide it as the
value of the strategy attribute of the @GeneratedValue annotation.

www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
@Entity
public class Book {

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


}

By default, Hibernate uses a sequence called hibernate_sequence.


You can also tell Hibernate to use one of your own database
sequences. I explained that in more details in Hibernate Tips: How to
use a custom database sequence.

Generate UUID Primary Keys


UUIDs and numerical primary keys might seem very different. But
with Hibernate, you can map and use them in almost the same way.
The only difference is the type of the primary key attribute, which is
a java.util.UUID instead of a java.lang.Long.
Here is a simple example. The Book entity maps an attribute of type
UUID and uses one of Hibernate’s generators to create primary key
values automatically before persisting a new entity.
@Entity
public class Book {

@Id
@GeneratedValue
private UUID id;


}

www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
Manage Composite Primary Keys
JPA and Hibernate also provide multiple ways to map composite
primary keys that consist of multiple attributes. Let’s take a look at
my preferred option: the embedded id.
The embedded id approach uses an embeddable to map the primary
key attributes. An embeddable is a pure Java class that is annotated
with @Embeddable. It defines attribute mappings in a reusable way. If
you want to use it as an embedded id, you also need to implement
the equals and hashCode methods.

@Embeddable
public class AddressKey implements Serializable {

private Long xId;


private Long yId;

public AddressKey() {}

public AddressKey(Long xId, Long yId) {


this.xId = xId;
this.yId = yId;
}

// getter and setter methods

@Override
public int hashCode() {
int result = 1;
result = 31 * result + ((xId == null) ? 0 : xId.hashCode());
result = 31 * result + ((yId == null) ? 0 : yId.hashCode());
return result;
}

@Override

www.thoughts-on-java.org
public boolean equals(Object obj) {
5 Primary Key Mappings Every Developer Should Know
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AddressKey other = (AddressKey) obj;
if (xId == null) {
if (other.xId != null)
return false;
} else if (!xId.equals(other.xId))
return false;
if (yId == null) {
if (other.yId != null)
return false;
} else if (!yId.equals(other.yId))
return false;
return true;
}
}

You can then use the embeddable class as the type of your primary
key attribute and annotate it with @EmbeddedId. The embeddable
and all its attributes become part of the entity. It follows the same
lifecycle, and all its attributes get mapped to the database table that’s
mapped by the entity.

www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
@Entity
public class Address {

@EmbeddedId
private AddressKey id;

private String city;

private String street;

private String country;

@OneToOne(mappedBy = "address")
private Person person;

...
}

Use Same Primary Key Values for Associated Entities


Another common primary key mapping is to use the same primary
key value in a one-to-one association.
You can, of course, map this with JPA and Hibernate. The only things
you need to do are to model the owning side of the association on
the entity that shall reuse the primary key value and to add a
@MapsId annotation to it.

www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
@Entity
public class Manuscript {

@Id
private Long id;

private byte[] file;

@OneToOne
@JoinColumn(name = "id")
@MapsId
private Book book;

...
}

Work with Natural IDs


Most teams prefer to use a surrogate key as the primary key. It’s
easier to manage in your code, and all involved systems can handle it
more efficiently. But modeling a natural ID is still useful. You will,
most likely, reference them very often in your use cases.
Hibernate provides an annotation to declare a natural ID and an API
to retrieve entities by it. Let’s take a quick look at the most important
details. And if you want to dive deeper, please read my article
@NaturalId – A good way to persist natural IDs with Hibernate?
You can specify a natural ID by annotating one or more entity
attributes with @NaturalId. I use it in the following code snippet, to
tell Hibernate that the isbn attribute is a natural ID of the Book
entity.

www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
Entity
public class Book {

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

@NaturalId
private String isbn;


}

After you’ve done that, you can use the byNaturalId method on
Hibernate’s Session interface to create a query that loads an entity by
its natural id.
In the next step, you need to provide the value of the natural id by
calling the using method for each attribute that’s part of the natural
id. In this example, the natural id only consists of the isbn attribute,
which I reference using the JPA metamodel class of the Book entity.
And after you provided the natural id value, you can call the load
method to execute the query.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);

Book b = session.byNaturalId(Book.class)
.using(Book_.isbn.getName(), “978-0321356680”).load();

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
Association Mappings
Association mappings are one of the key features of JPA and
Hibernate. They model the relationship between two database tables
as attributes in your domain model. That allows you to easily navigate
the associations in your domain model and JPQL or Criteria queries.

JPA and Hibernate support the same associations as you know from
your relational database model. You can use:
 one-to-one associations,
 many-to-one associations and
 many-to-many associations.
You can map each of them as a uni- or bidirectional association. That
means you can either model them as an attribute on only one of the
associated entities or on both. That has no impact on your database
mapping, but it defines in which direction you can use the
relationship in your domain model and JPQL or Criteria queries

Many-to-One Associations
An order consists of multiple items, but each item belongs to only
one order. That is a typical example for a many-to-one association. If
you want to model this in your database model, you need to store the
primary key of the Order record as a foreign key in the OrderItem
table.

Unidirectional Many-to-One Association


As you can see in the following code snippet, you can model this
association with an attribute of type Order and a @ManyToOne
annotation. The Order order attribute models the association, and the
annotation tells Hibernate how to map it to the database. You can use
the optional @JoinColumn annotation to define the name of the foreign
key column.

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
@Entity
public class OrderItem {

@ManyToOne
@JoinColumn(name = “fk_order”)
private Order order;


}

Unidirectional One-to-Many Association


The basic mapping definition is very similar to the many-to-one
association. It consist of the List items attribute which stores the
associated entities and a @OneToMany association.
But this is most likely not the mapping you’re looking for because
Hibernate uses an association table to map the relationship. If you want
to avoid that, you need to use a @JoinColumn annotation to specify the
foreign key column.

@Entity
public class Order {

@OneToMany
@JoinColumn(name = “fk_order”)
private List items = new ArrayList();


}

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
Bidirectional Many-to-One Associations
The bidirectional Many-to-One association mapping is the most
common way to model this relationship with JPA and Hibernate. It
uses an attribute on the Order and the OrderItem entity. This allows
you to navigate the association in both directions in your domain
model and your JPQL queries.

The mapping definition consists of 2 parts:


 the to-many side of the association which owns the relationship
mapping and
 the to-one side which just references the mapping

The mapping specification of the owning side is identical to the


unidirectional mapping.

@Entity
public class OrderItem {

@ManyToOne
@JoinColumn(name = “fk_order”)
private Order order;


}

The definition of the referencing part is a lot simpler. You just need to
reference the owning association mapping. You can do that by
providing the name of the association-mapping attribute to the
mappedBy attribute of the @OneToMany annotation. In this example,
that’s the order attribute of the OrderItem entity.

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
@Entity
public class Order {

@OneToMany(mappedBy = “order”)
private List items = new ArrayList();


}

Adding and removing entities from a bidirectional association


requires you to update both sides of the relationship. That is an error-
prone task, and a lot of developers prefer to implement it in a utility
method which updates both entities.

@Entity
public class Order {

public void addItem(OrderItem item) {


this.items.add(item);
item.setOrder(this);
}

}

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
Many-to-Many Associations
Many-to-Many relationships are another often used association type.
On the database level, it requires an additional association table
which contains the primary key pairs of the associated entities. But
as you will see, you don’t need to map this table to an entity.

A typical example for such a many-to-many association are Products


and Stores. Each Store sells multiple Products and each Product gets
sold in multiple Stores.

Unidirectional Many-to-Many Associations


Similar to the previously discussed mappings, the unidirectional many-
to-many relationship mapping requires an entity attribute and a
@ManyToMany annotation.

@Entity
public class Store {

@ManyToMany
@JoinTable(name = “store_product”,
joinColumns = {@JoinColumn(name = “fk_store”)},
inverseJoinColumns =
{@JoinColumn(name = “fk_product”)})
private List<Product> products =
new ArrayList<Product>();


}

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
You can customize the join table with the @JoinTable annotation and its
attributes joinColumns and inverseJoinColumns. The joinColumns
attribute defines the foreign key columns for the entity on which you
define the association mapping. The inverseJoinColumns attribute
specifies the foreign key columns of the associated entity.

Bidirectional Many-to-Many Associations


The bidirectional relationship mapping allows you to navigate the
association in both directions. The association mapping consists of
an owning and a referencing part. The owning part provides all
mapping information and the referencing part only links to it.

The mapping of the owning side is identical to the unidirectional


many-to-many association mapping.

@Entity
public class Store {

@ManyToMany
@JoinTable(name = “store_product”,
joinColumns = {@JoinColumn(name = “fk_store”)},
inverseJoinColumns =
{@JoinColumn(name = “fk_product”)})
private List<Product> products =
new ArrayList<Product>();


}

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
The mapping for the referencing side of the relationship is a lot
easier. You just need to reference the attribute that owns the
association.

@Entity
public class Product{

@ManyToMany(mappedBy=”products”)
private List<Store> stores = new ArrayList<Store>();


}

You need to update both ends of a bidirectional association when you


want to add or remove an entity. It’s a good practice to provide
helper methods which update the associated entities.
@Entity
public class Store {

public void addProduct(Product p) {


this.products.add(p);
p.getStores().add(this);
}

public void removeProduct(Product p) {


this.products.remove(p);
p.getStores().remove(this);
}

}

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
One-to-One Associations
An example for a one-to-one association could be a Customer and
the ShippingAddress. Each Customer has exactly one
ShippingAddress and each ShippingAddress belongs to one
Customer. On the database level, this mapped by a foreign key
column either on the ShippingAddress or the Customer table.

Unidirectional One-to-One Associations


The required mapping is similar to the previously discussed
mappings. You need an entity attribute that represents the
association, and you have to annotate it with an @OneToOne
annotation.

@Entity
public class Customer{

@OneToOne
@JoinColumn(name = “fk_shippingaddress”)
private ShippingAddress shippingAddress;


}

Bidirectional One-to-One Associations


The bidirectional one-to-one relationship mapping extends the
unidirectional mapping with an association-referencing side so that you
can navigate it in both direction.

www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
The definition of the owning side of the mapping is identical to the
unidirectional mapping.

@Entity
public class Customer{

@OneToOne
@JoinColumn(name = “fk_shippingaddress”)
private ShippingAddress shippingAddress;


}

The referencing side of the association just links to the attribute that
owns the relationship.

@Entity
public class ShippingAddress{

@OneToOne(mappedBy = “shippingAddress”)
private Customer customer;


}

www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings

Don’t use unidirectional one-to-many associations


In your table model, you normally use a foreign key column on the
to-many side of the association to store a reference to the associated
record. Hibernate uses the same approach when you model a
bidirectional one-to-many or an unidirectional many-to-one
relationship. It uses the foreign key column to map the association.
But it can’t do that if you don’t model the relationship on the entity,
which represents the to-many side of the relationship. So, Hibernate
introduces an association table to store the foreign keys.
You can avoid this table if you specify the foreign key column with a
@JoinColumn annotation. This column has to be part of the table of
the to-many side of the association.

@Entity
public class PurchaseOrder {

@OneToMany
@JoinColumn(name = "fk_order")
private Set<Item> items = new HashSet<Item>();

...
}

Hibernate now uses the foreign key column instead of an association


table to map the relationship. But it still has to perform an additional
SQL UPDATE statement to set the foreign key because the Item
entity doesn’t map the foreign key column.

www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings

Avoid the mapping of huge to-many associations


Hibernate loads all associated entities when it initializes an
association. That can take several seconds or even minutes when
Hibernate has to fetch several thousand entities.
So, better use an unidirectional many-to-one association. You can’t
use the to-many mapping anyways, and it removes the risk that
someone triggers the initialization by accident.
If you need to join the associated entities in a JPQL query, you can
either use the mapped many-to-one association or a Hibernate-
specific JOIN clause that doesn’t require a mapped relationship.

Think twice before using CascadeType.Remove


Cascade remove is another feature that works well on small to-many
associations. But it’s very inefficient when it needs to remove a huge
number of entities.
Hibernate needs to execute proper lifecycle transitions for all
entities. So, Hibernate needs to select all associated Item entities and
remove them one by one.
You can use a JPQL query to remove all Item entities with 1
statement. But please be aware that Hibernate will not call any
EntityListeners for these entities, and it also doesn’t remove them
from any caches.
You can update the caches programmatically. The following code
snippet shows an example that removes all entities from the first
level cache before it calls a JPQL query to remove all Item entities
associated to a given Order entity.

www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings
em.flush();
em.clear();

Query q = em.createQuery("DELETE Item i WHERE


i.order.id = :orderId");
q.setParameter("orderId", orderId);
q.executeUpdate();

order = em.find(PurchaseOrder.class, orderId);


em.remove(order);

Use orphanRemoval when modeling parent-child


associations
The orphanRemoval feature can make it very comfortable to remove
a child entity. You can use it for parent-child relationships in which a
child entity can’t exist without its parent entity.
When you set the orphanRemoval attribute of the @OneToMany
annotation to true and the cascade attribute to CascadeType.ALL,
Hibernate automatically removes a child entity when you remove it
from the association.

@Entity
public class PurchaseOrder {

@OneToMany(mappedBy = "order",
orphanRemoval = true)
private List<Item> items = new ArrayList<Item>();
}

www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings

Implement helper methods to update bi-directional


associations
When you add an entity to or remove it from a bi-directional
association, you need to perform the operation on both ends.
That is an error-prone task. You should, therefore, provide helper
methods that implement this logic.

@Entity
public class PurchaseOrder {

...

public void addItem(Item item) {


this.items.add(item);
item.setOrder(this);
}
}

Define FetchType.LAZY for @ManyToOne association


The JPA specification defines FetchType.EAGER as the default for to-
one relationships. It tells Hibernate to initialize the association, when
it loads the entity.
That becomes a problem when you load multiple entities. Hibernate
then needs to perform an additional query for each of the selected
entities. That is often called a n+1 select issue.
You can avoid that by setting the FetchType on the @ManyToOne
annotation to LAZY.

www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings
@Entity
public class Item {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "fk_order")
private PurchaseOrder order;

...
}

And if you need the to-one association in your use case, you can use
a JOIN FETCH clause or one of the other options to initialize lazy
relationships.

List<Item> items = em.createQuery(


"SELECT i FROM Item i JOIN FETCH i.order",
Item.class)
.getResultList();

www.thoughts-on-java.org
Best Practices for Many-to-Many Associations
Many-to-Many associations are one of the most commonly used
associations with JPA and Hibernate. You can find lots of examples
for them in the real world, and you can map them with JPA and
Hibernate as a uni- or bidirectional association in your domain
model.
But you probably also know that these mappings provide several
pitfalls. In this article, I will show you 5 best practices that will help
you to avoid these pitfalls and to implement efficient mappings.

1. The most efficient data type for your association


Hibernate handles remove operations on Many-to-Many
relationships that are mapped to a java.util.List very inefficiently. It
first removes all records from the association table before it inserts
all remaining ones.
You should instead model a many-to-many association as a
java.util.Set.

@Entity
public class Book {

@ManyToMany
@JoinTable(name = "book_author",
joinColumns = { @JoinColumn(name = "fk_book") },
inverseJoinColumns = { @JoinColumn(name = "fk_author") })
private Set authors = new HashSet();

...
}

www.thoughts-on-java.org
Best Practices for Many-to-Many Associations
2. Why you need utility methods to manage your
association
Bidirectional associations are mapped to an entity attribute on both
ends of the relationships. If you add or remove an association, you
always need to perform the change on both ends of the association.
Utility methods your entities make updating and removing much easier.
Within this methods, you perform the required operations on both
entities.

@Entity
public class Author {

@ManyToMany(mappedBy = "authors")
private List<Book> books = new ArrayList<Book>();

...

public void addBook(Book book) {


this.books.add(book);
book.getAuthors().add(this);
}

public void removeBook(Book book) {


this.books.remove(book);
book.getAuthors().remove(this);
}
}

www.thoughts-on-java.org
Best Practices for Many-to-Many Associations
3. The right FetchType for an efficient mapping
You should always use FetchType.LAZY for your many-to-many
associations. It tells your persistence provider not to fetch the
associated entities from the database until you use them. That’s
usually the case when you call its getter method for the first time.
Luckily, that’s the default for all to-many associations. So, please
make sure that you don’t change it.

4. When and how to use query-specific fetching


When you load an entity and use query-specific fetching, you tell
Hibernate which mapped associations it shall initialize for each
fetched entity. It then extends the SELECT clause of your query so
that it includes the columns mapped by these other entities and
initializes the associations.
You can implement query-specific fetching in several different ways.
The simplest one is a JOIN FETCH clause.

Author a = em.createQuery(
"SELECT a FROM Author a JOIN FETCH a.books “
+ “WHERE a.id = 1", Author.class).getSingleResult();
It not only gets translated into a SQL JOIN, as it’s the case for a JPQL
JOIN clause, it also forces your persistence provider to extend the
SELECT clause by all columns that are mapped by the associated
entity.

16:21:03,046 DEBUG SQL:94 -


select
author0_.id as id1_0_0_,
book2_.id as id1_1_1_,
author0_.firstName as firstNam2_0_0_,
author0_.lastName as lastName3_0_0_,
author0_.version as version4_0_0_,
book2_.format as format2_1_1_,
book2_.publishingDate as publishi3_1_1_,
www.thoughts-on-java.org
Best Practices for Many-to-Many Associations
book2_.format as format2_1_1_,
book2_.publishingDate as publishi3_1_1_,
book2_.title as title4_1_1_,
book2_.version as version5_1_1_,
books1_.author_id as author_i2_2_0__,
books1_.book_id as book_id1_2_0__
from
Author author0_
inner join
book_author books1_
on author0_.id=books1_.author_id
inner join
Book book2_
on books1_.book_id=book2_.id
where
author0_.id=1

5. The CascadeType you should avoid at all costs


If you activate cascading on an association, your persistence provider
applies the operations you perform on the entity to all associated
entities. If it does that for all operations or just for a few selected
ones depends on the configured CascadeType.
You should avoid the CascadeTypes REMOVE and ALL, which
includes REMOVE, for many-to-many associations. In the best case,
it only creates performance issues, but in the worst case, it might
also remove more records than you intended.
So, better remove associated entities programmatically.

www.thoughts-on-java.org
The Most Efficient Data Type For To-Many Associations

The difference between a Bag and a List


Hibernate’s naming of the different collection types is a little bit
confusing because Lists and Bags are both mapped by a java.util.List.
The difference between them is that a List is ordered and a Bag is
unordered.
You should prefer the unordered Bag for most mappings because
retrieving the association in a specific order slows down your
database queries. You should better use a JPQL query with an ORDER
BY clause to define the ordering if you need it.

Should you use a Bag or a Set?


When you just look at the Java types, the answer seems to be easy. In
general, a java.util.List provides the better performance while a
java.util.Set doesn’t contain any duplicates. As long as you implement
the create use case correctly, a java.util.List seems like the obvious
best choice for your association mapping.
But it’s not that easy. A List might be more efficient than a Set, but
the type also influences how Hibernate manages the association in
the database. So, there are a few other things you need to take into
account when you make your decision.

A critical bug in older Hibernate versions


If you’re using a Hibernate version older than 5.0.8, you should be
aware of bug HHH-5855. When you used a java.util.List and merged
the parent entity, Hibernated generated 2 INSERT statements for
each new child entity.

www.thoughts-on-java.org
The Most Efficient Data Type For To-Many Associations

Inefficient handling of many-to-many associations


When you’re mapping a many-to-many association, you should
always use a java.util.Set.

@Entity
public class Book {

@ManyToMany
@JoinTable(name = "book_author",
joinColumns = { @JoinColumn(name = "fk_book") },
inverseJoinColumns = { @JoinColumn(name = "fk_author") })
private Set authors = new HashSet();

...
}

If you model the association as a java.util.List, Hibernate handles the


removal of associated entities very inefficiently. Instead of removing
the record that maps the removed association, Hibernate removes all
records from the association table before it inserts a new record for the
remaining associations.

Summary
As you’ve seen, mapping an association as a java.util.List can create
problems which by far outweigh the small performance gain you get
compared to a java.util.Set. So, better make sure to update your
Hibernate version and to use a Set to model many-to-many
associations.

www.thoughts-on-java.org
Hibernate Version > 5.0.8?

Yes No

Yes
Many-to-Many Association?

No

Use a java.util.List Use a java.util.Set

www.thoughts-on-java.org
Composition vs. Inheritance with JPA and Hibernate
Like all object-oriented programing languages, Java supports the
fundamental concepts of inheritance and composition. There is an
important difference between both concepts. Inheritance enables
you to model an is-a association between two classes by extending a
superclass. Composition models a has-a association by referencing
another class in an instance variable.
For plain Java classes, there have been hundreds of discussions about
this question. And there is a clear winner: You should prefer
composition unless the classes are specifically designed for
extension.
But all these discussions and recommendations are for plain Java
classes. Is this also valid for entity classes annotated with JPA
annotations and mapped to tables in a relational database? And how
should you implement the mapping?

Inheritance vs. Composition for Entity Classes


The general recommendation to use composition over inheritance is
also valid for entity classes. In addition to all arguments that apply to
plain Java classes, there is another reason to use composition to
implement your entity classes. When you are working with entities,
you always need to keep in mind that your classes will be mapped to
database tables.
Relational databases don’t support the inheritance concept. You have
to use one of JPA’s inheritance strategies to map your inheritance
hierarchy to one or more database tables. When you choose one of
these strategies, you need to decide:
 if you want to forgo constraints that ensure data consistency so
that you get the best performance, or
 if you accept inferior performance so that you can use the
constraints.
As you can see, both approaches have their disadvantages. You don’t
run into these problems if you use composition.

www.thoughts-on-java.org
Composition vs. Inheritance with JPA and Hibernate

Using Composition with JPA and Hibernate


JPA and Hibernate support 2 basic ways to implement composition:
1. You can reference another entity in your composition by
modeling an association to it. Each entity will be mapped to its
database table and can be loaded independently. A typical
example is a Person entity which has a one-to-many
association to an Address entity.
2. You can also use an embeddable to include its attributes and
their mapping information into your entity mapping. The
attributes are mapped to the same table as the other attributes
of the entity. The embeddable can’t exist on its own and has no
persistent identity. You can use it to define a composition of
address attributes which become part of the Person entity.

Composition via association mapping


For a lot of developers, this is the most natural way to use
composition in entity mappings. It uses concepts that are well-
established and easy to use in both worlds: the database, and the Java
application.
Associations between database records are very common and easy to
model. You can:
 store the primary key of the associated record in one of the
fields of your database record to model a one-to-many
association. A typical example is a record in the Book table
which contains the primary key of a record in the Publisher
table. This enables you to store a reference to the publisher
who published the book.
 introduce an association table to model a many-to-many
relationship. Each record in this table stores the primary key of
the associated record. E.g., a BookAuthor table stores the
primary keys of records in the Author and Book tables to persist
which authors wrote a specific book.
One of the main benefits of JPA and Hibernate is that you can easily
map these associations in your entities. You just need an attribute of

www.thoughts-on-java.org
Composition vs. Inheritance with JPA and Hibernate
the type of the associated entity or a collection of the associated
entities and a few annotation. I explained these mappings in great
details in one of my previous posts: Ultimate Guide – Association
Mappings with JPA and Hibernate.

Composition with embeddables


Embeddables are another option to use composition when
implementing your entities. They enable you to define a reusable set
of attributes with mapping annotations. The main difference to the
previously discussed association mapping is that an embeddable
becomes part of the entity and has no persistent identity on its own.

Let’s take a look at an example. The 3 attributes of the Address class


store simple address information. The @Embeddable annotation tells
Hibernate and any other JPA implementation that this class and its
mapping annotations can be embedded into an entity. In this
example, I rely on JPA’s default mappings and don’t provide any
mapping information.
@Embeddable
public class Address {

private String street;


private String city;
private String postalCode;

...
}

After you have defined your embeddable, you can use it as the type of
an entity attribute. You just need to annotate it with @Embedded,
and your persistence provider will include the attributes and
mapping information of your embeddable in the entity. So, in this

www.thoughts-on-java.org
Composition vs. Inheritance with JPA and Hibernate
example, the attributes street, city and postalCode of the
embeddable Address will be mapped to columns with the same
names of the Author table.

@Entity
public class Author implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Embedded
private Address address;

...
}

If you want to use multiple attributes of same embeddable type, you


need to override the column mapping of the attributes of the
embeddable. You can do that with a collection of @AttributeOverride
annotations. Since JPA 2.2, the @AttributeOverride is repeatable, and
you no longer need to wrap it in an @AttributeOverrides annotation.

www.thoughts-on-java.org
Composition vs. Inheritance with JPA and Hibernate
@Entity
public class Author implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Embedded
private Address privateAddress;

@Embedded
@AttributeOverride(
name = "street",
column = @Column( name = "business_street" )
)
@AttributeOverride(
name = "city",
column = @Column( name = "business_city" )
)
@AttributeOverride(
name = "postalCode",
column = @Column( name = "business_postcalcode" )
)
private Address businessAddress;

...
}

www.thoughts-on-java.org
Inheritance strategies with JPA and Hibernate
Mapped Superclass
The mapped superclass strategy is the simplest approach to mapping
an inheritance structure to database tables. It maps each concrete
class to its own table.

That allows you to share the attribute definition between multiple


entities. But it also has a huge drawback. A mapped superclass is not
an entity, and there is no table for it.
That means that you can’t use polymorphic queries that select all
Publication entities and you also can’t define a relationship between
an Author entity and all Publications.
If you just want to share state and mapping information between
your entities, the mapped superclass strategy is a good fit and easy to
implement. You just have to set up your inheritance structure,
annotate the mapping information for all attributes and add the
@MappedSuperclass annotation to your superclass.

@MappedSuperclass
public abstract class Publication {…}

The subclasses Book and BlogPost extend the Publication class and
add their specific attributes with their mapping annotations. Both
classes are also annotated with @Entity and will be managed by the
persistence provider.

@Entity(name = “Book”)
public class Book extends Publication {…}

@Entity(name = “BlogPost”)
public class BlogPost extends Publication {…}

www.thoughts-on-java.org
Inheritance strategies with JPA and Hibernate
Table per Class
The table per class strategy is similar to the mapped superclass
strategy. The main difference is that the superclass is now also an
entity. Each of the concrete classes gets still mapped to its own
database table. This mapping allows you to use polymorphic queries
and to define relationships to the superclass. But the table structure
adds a lot of complexity to polymorphic queries, and you should
avoid them.

The definition of the superclass with the table per class strategy
looks similar to any other entity definition. You annotate the class
with @Entity and add your mapping annotations to the attributes.
The only difference is the additional @Inheritance annotation which
you have to add to the class to define the inheritance strategy. In this
case, it’s the InheritanceType.TABLE_PER_CLASS.

@Entity
@Inheritance(strategy =
InheritanceType.TABLE_PER_CLASS)
public abstract class Publication {…}

The definitions of the Book and BlogPost entities are identical to the
previously discussed mapped superclass strategy. You just have to
extend the Publication class, add the @Entity annotation and add the
class specific attributes with their mapping annotations.

www.thoughts-on-java.org
Inheritance strategies with JPA and Hibernate
@Entity(name = “Book”)
public class Book extends Publication {…}

@Entity(name = “BlogPost”)
public class BlogPost extends Publication {…}

Single Table
The single table strategy maps all entities of the inheritance
structure to the same database table. This approach makes
polymorphic queries very efficient and provides the best
performance.
But it also has some drawbacks. The attributes of all entities are
mapped to the same database table. Each record uses only a subset
of the available columns and sets the rest of them to null. You can,
therefore, not use not null constraints on any column that isn’t
mapped to all entities. That can create data integrity issues, and your
database administrator might not be too happy about it.

When you persist all entities in the same table, Hibernate needs a
way to determine the entity class each record represents. This is
information is stored in a discriminator column which is not an entity
attribute. You can either define the column name with a
@DiscriminatorColumn annotation on the superclass or Hibernate
will use DTYPE as its default name.

www.thoughts-on-java.org
Inheritance strategies with JPA and Hibernate
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = “Publication_Type”)
public abstract class Publication {…}

The definition of the subclasses is again similar to the previous


examples. But this time, you should also provide a
@DiscriminatorValue annotation. It specifies the discriminator value
for this specific entity class so that your persistence provider can
map each database record to a concrete entity class.

@Entity(name = “Book”)
@DiscriminatorValue(“Book”)
public class Book extends Publication {…}

@Entity(name = “BlogPost”)
@DiscriminatorValue(“Blog”)
public class BlogPost extends Publication {…}

Joined
The joined table approach maps each class of the inheritance
hierarchy to its own database table. This sounds similar to the table
per class strategy. But this time, also the abstract superclass
Publication gets mapped to a database table. This table contains
columns for all shared entity attributes. The tables of the subclasses
are much smaller than in the table per class strategy. They hold only
the columns specific for the mapped entity class and a primary key
with the same value as the record in the table of the superclass.

www.thoughts-on-java.org
Inheritance strategies with JPA and Hibernate

Each query of a subclass requires a join of the 2 tables to select the


columns of all entity attributes. That increases the complexity of
each query, but it also allows you to use not null constraints on
subclass attributes and to ensure data integrity. The definition of the
superclass Publication is similar to the previous examples. The only
difference is the value of the inheritance strategy which is
InheritanceType.JOINED.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Publication {…}

The definition of the subclasses doesn’t require any additional


annotations. They just extend the superclass, provide an @Entity
annotation and define the mapping of their specific attributes.

@Entity(name = “Book”)
public class Book extends Publication {…}

@Entity(name = “BlogPost”)
public class BlogPost extends Publication {…}

www.thoughts-on-java.org
Inheritance strategies with JPA and Hibernate
Choosing a Strategy
Choosing the right inheritance strategy is not an easy task. As so
often, you have to decide which advantages you need and which
drawback you can accept for your application. Here are a few
recommendations:
 If you require the best performance and need to use
polymorphic queries and relationships, you should choose the
single table strategy. But be aware, that you can’t use not null
constraints on subclass attributes which increase the risk of
data inconsistencies.
 If data consistency is more important than performance and
you need polymorphic queries and relationships, the joined
strategy is probably your best option.
 If you don’t need polymorphic queries or relationships, the
table per class strategy is most likely the best fit. It allows you
to use constraints to ensure data consistency and provides an
option of polymorphic queries. But keep in mind, that
polymorphic queries are very complex for this table structure
and that you should avoid them.

www.thoughts-on-java.org
Difference between persist/save & merge/update
JPA and Hibernate provide different methods to persist new and to
update existing entities. You can choose between JPA’s persist and
merge and Hibernate’s save and update methods.
It seems like there are 2 pairs of 2 methods that do the same. You
can use the methods persist and save to store a new entity and the
methods merge and update to store the changes of a detached entity
in the database.
So, which methods should you use?

Entity State Transitions


Let’s take a quick look at JPA’s entity lifecycle states.

Persisting A New Entity Using persist Or save


When you create a new entity object, it’s in the transient lifecycle
state. It does not map any database record. You need to attach the
entity to a persistence context so that it becomes managed and gets
persisted in the database. You can either use JPA’s persist or
Hibernate’s save method to do that. Both methods seem to do the
same, but there are a few differences.

www.thoughts-on-java.org
Difference between persist/save & merge/update

Specification vs. Proprietary API


The most obvious difference is that the JPA specification defines the
persist method. You can use it with all JPA implementations. The save
method, on the other hand, is Hibernate-specific. It is, therefore, not
available in other JPA implementations.

Return Types And Execution Of SQL Statements


Another obvious difference between these 2 methods is their return
type. JPA’s persist method returns void and Hibernate’s save method
returns the primary key of the entity.
That might seem like a huge difference, especially when you consider
that Hibernate’s Javadoc states that the primary key gets generated
immediately while the JPA specification doesn’t define when the
primary key has to be generated.
In most cases, it doesn’t make any difference if you call the save or
persist method. Hibernate uses the name of the entity class and the
primary key value to store the entity in the first level cache. It,
therefore, needs a primary key value when it executes the persist
method.
In almost all situations, Hibernate generates the primary key value
immediately and triggers an SQL statement if necessary, when you
call the persist or save method.
But that is not the case if you use the IDENTITY strategy and try to
persist an entity without an active transaction or with
FlushMode.MANUAL. If you call the persist method in one of these
situations, Hibernate delays the execution of the SQL INSERT
statement and creates a temporary primary key value. But if you call
the save method, Hibernate performs the SQL INSERT statement
immediately and retrieves the primary key value from the database.

www.thoughts-on-java.org
Difference between persist/save & merge/update

Which one to choose?


You could expect that the save and persist method behave differently
because there are a few differences between the JPA specification
and the Javadoc of Hibernate’s proprietary methods.
But almost all of these differences disappear when you take a look at
the internal implementation. The only ones that remain are 2 corner
cases in which Hibernate might delay the retrieval of the primary key,
the return type of the method and the support by other JPA
implementations.
For most applications, it doesn’t make any difference if you get the
generated primary key value as the return type of Hibernate’s save
method or from the getter method of your primary key attribute. As
long as you don’t use an extended persistence context and perform
all database operations with an active transaction, I recommend
using JPA’s persist method.

Updating a detached entity


You can use Hibernate’s update or JPA’s merge method to associate a
detached entity with a persistence context. After you’ve done that,
Hibernate will update the database based on the entity attribute
values.
The effect of the update and merge method seem to be the same, but
as you will see in the following sections, there is an important
difference.

JPA’s merge method


JPA’s merge method copies the state of a detached entity to a
managed instance of the same entity. Hibernate, therefore, executes
an SQL SELECT statement to retrieve a managed entity from the
database. If the persistence context already contained a managed
instance of the entity, Hibernate uses the existing one instead. It
then copies all attribute values to the managed entity and returns it
to the caller.

www.thoughts-on-java.org
Difference between persist/save & merge/update
When Hibernate flushes the persistence context for the next time, its
dirty checking mechanism checks all managed entities. If it detects
that the merge operation changed any entity attribute value, it
triggers the required SQL UPDATE statement.
There is one important detail you need to know when you use JPA’s
merge method. Hibernate copies the attribute values of the detached
entity to the managed entity. This overwrites any changes that you
performed on this entity within the current Session.

Hibernate’s update method


Hibernate’s update method doesn’t trigger an SQL SELECT
statement. It just attaches the entity to the current persistence
context. In contrast to JPA’s merge method, you can’t lose any
changes by calling the update method. If the persistence context
already contains a managed instance of the entity you want to
update, it throws an exception.
When Hibernate performs the next flush, it doesn’t perform any dirty
checks. That’s not possible because Hibernate didn’t read the latest
version of the entity from the database. It just executes an SQL
UPDATE statement for the reattached entity.
The missing dirty check causes an unnecessary SQL UPDATE
statement when the entity and the corresponding database record
contain the same values. This might be a problem if your DBA
registered an update trigger for the database table. In these
situations, you can annotate your entity with @SelectBeforeUpdate.
That tells Hibernate to select the entity and perform a dirty check
before it generates the SQL UPDATE statement. The behavior of the
update method is now similar to JPA’s merge method.

Which one to choose?


There is no general answer to this questions. As you have seen, both
methods have their advantages and disadvantages. You have to
decide for your specific use case if Hibernate needs to select the
entity before it triggers the SQL UPDATE statement. And if that’s the

www.thoughts-on-java.org
Difference between persist/save & merge/update
case, you also need to consider the depth of your entity graph and
the performance implications of the provided fetching behavior.

Updating a managed entity


JPA and Hibernate make it very easy to update a managed entity. If
your entity is in the lifecycle state managed, e.g. because you fetched
it with a JPQL query or the find method of the EntityManager, you
just need to change the values of your entity attributes. When
Hibernate decides to flush the persistence context, the dirty checking
mechanism will detect the change and perform the required SQL
UPDATE statement.
You don’t need to, and you should not call Hibernate’s save method
after you updated an entity. That triggers an additional
SaveOrUpdate event without providing any benefits. When Hibernate
decides to flush the persistence context, it will perform the dirty
check anyway to detect all changes before it executes the required
SQL UPDATE statements.

www.thoughts-on-java.org
Difference between persist/save & merge/update

CREATE OR REPLACE FUNCTION calculate(IN x double


precision, IN y double precision, OUT sum double precision)
RETURNS double precision AS $BODY$ BEGIN sum = x +
y; END; $BODY$ LANGUAGE plpgsql VOLATILE COST
100;
CREATE TABLE book (id bigint NOT NULL, publishingdate
date, title character varying(255), price double precision,
version integer, CONSTRAINT book_pkey PRIMARY KEY
(id));
CREATE SEQUENCE hibernate_sequence INCREMENT 1
MINVALUE 1 MAXVALUE 9223372036854775807 START 1
CACHE 1;

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
Databases support various data types to store date and time
information. The most commonly used ones are:
 DATE to save a date without time information,
 TIME to store a time without a date, and
 TIMESTAMP to store date and time information.
You can map all of them with JPA and Hibernate.
But you need to decide to which Java type you want to map your
database column. The Java language supports a bunch of classes to
represent date and time information, like:
 java.util.Date and java.util.Calendar
 java.sql.Date, java.sql.Time and java.sql.Timestamp
 java.time.LocalDate, java.time.LocalDateTime,
java.time.OffsetTime, java.time.OffsetDateTime,
java.time.ZonedDateTime, java.time.Duration
JPA supports most of them. In addition to that, Hibernate provides
proprietary support for almost all of the remaining ones.

Mapping java.util classes


Before the release of Java 8, java.util.Date and java.util.Calendar were
the most commonly used classes to represent dates with and without
time information.
You can, of course, map both of them with JPA and Hibernate. But
the mapping requires a few additional information. You need to
define if you want to map the java.util.Date or java.util.Calendar to a
column of type DATE, TIME or TIMESTAMP.
You can do that by annotating the entity attribute with @Temporal
and providing a TemporalType enum value as a value. You can choose
between:
 TemporalType.DATE to map it to a SQL DATE column
 TemporalType.TIME to map it to a SQL TIME column

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
 TemporalType.TIMESTAMP to map it to a SQL TIMESTAMP
column
I’m using the @Temporal annotation in the following code snippet to
map an attribute of type java.util.Date to a TIMESTAMP column and
an attribute of type java.util.Calendar to a DATE column.

@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Temporal(TemporalType.TIMESTAMP)
private Date utilDate;

@Temporal(TemporalType.DATE)
private Calendar utilCalendar;

...
}

You can then use these attributes in the same way you use any other
entity attributes.

MyEntity e = new MyEntity();


e.setUtilDate(new Date(119, 6, 18));
e.setUtilCalendar(new GregorianCalendar(2019, 6, 18));
em.persist(e);

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
If you activate the logging of SQL statements, you can find the
following messages in your log file.

16:04:07,185 DEBUG SQL:92 - insert into MyEntity (utilCalendar, utilDate, id)


values (?, ?, ?)
16:04:07,202 TRACE BasicBinder:65 - binding parameter [8] as [DATE] -
[java.util.GregorianCalendar[time=1563400800000,areFieldsSet=true,areAllFields
Set=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3
600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.
SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDayli
ght=true,startYear=0,startMode=2,startMonth=2,startDay=-
1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMont
h=9,endDay=-
1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=2,min
imalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=6,WEEK_OF_YEAR=29,W
EEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=199,DAY_OF_WE
EK=5,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,
MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=3600000,DST_OFF
SET=3600000]]
16:04:07,207 TRACE BasicBinder:65 - binding parameter [2] as [TIMESTAMP] -
[Thu Jul 18 00:00:00 CEST 2019]
16:04:07,208 TRACE BasicBinder:65 - binding parameter [3] as [BIGINT] - [1]

The 2nd message about the binding of the GregorianCalendar might


surprise you. That’s Hibernate’s very complicated way to show you
which Calendar object gets bound to a parameter of type DATE. But
don’t worry, if you take a look at the database, you can see that
Hibernate wrote the date to a column of type DATE.

Mapping java.sql classes


The mappings of the java.sql classes Date, Time, and Timestamp are
easier than the previously shown mappings of the java.util classes.

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
That’s because the classes in the java.sql package match the SQL data
types.
That enables your persistence provider, e.g., Hibernate, to identify
the mapping automatically. So, without providing any additional
annotations:
 java.sql.Date gets mapped to SQL DATE,
 java.sql.TIME gets mapped to SQL TIME and
 java.sql.TIMESTAMP gets mapped to SQL TIMESTAMP.

@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private java.sql.Date sqlDate;


private Time sqlTime;
private Timestamp sqlTimestamp;

...
}

You can then use these attributes in your business code to store date
and time information in your database.
MyEntity e = new MyEntity();
e.setSqlDate(new java.sql.Date(119, 6, 18));
e.setSqlTime(new Time(15, 05, 30));
e.setSqlTimestamp(new Timestamp(119, 6, 18, 15, 05, 30, 0));
em.persist(e);

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
And after you activated the logging of SQL statements, you can see
that Hibernate maps the entity attributes to the corresponding SQL
types.

06:33:09,139 DEBUG SQL:92 - insert into MyEntity (sqlDate, sqlTime,


sqlTimestamp, id) values (?, ?, ?, ?)
06:33:09,147 TRACE BasicBinder:65 - binding parameter [1] as [DATE] - [2019-
07-18]
06:33:09,147 TRACE BasicBinder:65 - binding parameter [2] as [TIME] -
[15:05:30]
06:33:09,147 TRACE BasicBinder:65 - binding parameter [3] as [TIMESTAMP] -
[2019-07-18 15:05:30.0]
06:33:09,154 TRACE BasicBinder:65 - binding parameter [4] as [BIGINT] - [1]

Mapping java.time classes


Java 8 introduced the Date and Time API to fix the flaws of the
java.util.Date class. Using the new API is clean and concise, and you
can finally distinguish between date and time information.
Since Hibernate 5 and JPA 2.2, you can use the following classes as
attribute types.
Java Type JPA Hibernate JDBC Type
java.time.LocalDate X X DATE
java.time.LocalTime X X TIME

java.time.LocalDateT X X TIMESTAMP
ime
java.time.OffsetTime X X TIME_WITH_TIMEZ
ONE
java.time.OffsetDate X X TIMESTAMP_WITH_
Time TIMEZONE
java.time.Duration - X BIGINT
java.time.Instant - X TIMESTAMP
java.time.ZonedDate - X TIMESTAMP
Time

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
As you can see in the table, Hibernate supports a few more Date and
Time classes than JPA. You can easily add support for additional
classes by implementing an AttributeConverter. I used it in a previous
article to map an attribute of type Duration with JPA.
Hibernate’s proprietary support for Duration and Instant works fine.
But you should be careful if you want to use Hibernate’s mapping of
ZonedDateTime. The handling of timezones and the mapping to a
TIMESTAMP column presents a few pitfalls. I get into more details
about that in the Working with ZonedDateTime section.
Let’s first take a look at a basic entity mapping.
@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private LocalDate localDate;


private LocalDateTime localDateTime;
private OffsetTime offsetTime;
private OffsetDateTime offsetDateTime;

// Hibernate-specific - not supported by JPA


private Duration duration;
private Instant instant;
private ZonedDateTime zonedDateTime;
...
}

In this example, I map entity attributes of the 7 types supported by


JPA. As you can see, these mappings don’t require any annotations.

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
MyEntity e = new MyEntity();
e.setLocalDate(LocalDate.of(2019, 7, 19));
e.setLocalDateTime(LocalDateTime.of(2019, 7, 19, 15, 05, 30));
e.setOffsetTime(OffsetTime.of(15, 05, 30, 0, ZoneOffset.ofHours(+2)));
e.setOffsetDateTime(OffsetDateTime.of(2019, 7, 19, 15, 05, 30, 0,
ZoneOffset.ofHours(+2)));

// Hibernate-specific - not supported by JPA


e.setDuration(Duration.ofHours(2));
e.setInstant(Instant.now());
e.setZonedDateTime(ZonedDateTime.of(2019, 7, 18, 15, 05, 30, 0,
ZoneId.of("UTC-4")));

em.persist(e);

The classes of the Date and Time API clearly define if they store date
and/or time information. So, the JPA specification and all
implementing frameworks can map them to the correct SQL types.
11:52:26,305 DEBUG SQL:94 - insert into MyEntity (duration, instant, localDate,
localDateTime, offsetDateTime, offsetTime, sqlDate, sqlTime, sqlTimestamp,
utilCalendar, utilDate, zonedDateTime, id) values (?, ?, ?, ?, ?, ?, ?, ?)
11:52:26,306 TRACE BasicBinder:65 - binding parameter [1] as [BIGINT] - [PT2H]
11:52:26,307 TRACE BasicBinder:65 - binding parameter [2] as [TIMESTAMP] -
[2019-07-22T09:52:26.284946300Z]
11:52:26,308 TRACE BasicBinder:65 - binding parameter [3] as [DATE] - [2019-
07-19]
11:52:26,308 TRACE BasicBinder:65 - binding parameter [4] as [TIMESTAMP] -
[2019-07-19T15:05:30]
11:52:26,312 TRACE BasicBinder:65 - binding parameter [5] as [TIMESTAMP] -
[2019-07-19T15:05:30+02:00]
11:52:26,313 TRACE BasicBinder:65 - binding parameter [6] as [TIME] -
[15:05:30+02:00]
11:52:26,324 TRACE BasicBinder:65 - binding parameter [7] as [TIMESTAMP] -
[2019-07-18T15:05:30-04:00[UTC-04:00]]
11:52:26,324 TRACE BasicBinder:65 - binding parameter [8] as [BIGINT] - [1]

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
Working with ZonedDateTime
As I mentioned early, using Hibernate’s support for ZonedDateTime
is risky. And let’s be honest, I don’t recommend using it.
Hibernate maps a ZonedDateTime to an SQL TIMESTAMP without
time zone information. It converts the ZonedDateTime into the local
time zone of the JVM and then stores it in the database. And when it
reads the TIMESTAMP, it adds the local time zone information to it.

MyEntity e = new MyEntity();


e.setZonedDateTime(ZonedDateTime.of(2019, 7, 18, 15, 05, 30, 0,
ZoneId.of("UTC-4")));
em.persist(e);

Hibernate shows the time zone information in the log message.

09:57:08,918 DEBUG SQL:92 - insert into MyEntity (zonedDateTime, id) values


(?, ?)
09:57:08,959 TRACE BasicBinder:65 - binding parameter [1] as [TIMESTAMP] -
[2019-07-18T15:05:30-04:00[UTC-04:00]]
09:57:08,961 TRACE BasicBinder:65 - binding parameter [2] as [BIGINT] - [1]

But you can see in the database, that it converted the time zone from
UTC-4 to UTC+2, which is my local time zone.

This mapping works as long as:


 you use a time zone without daylight saving time,
 all instances of your application use the same time zone, and
 you never have to change this time zone.
You can avoid these problems by configuring a time zone without
daylight saving time in your persistence.xml configuration. Hibernate

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
will then use the configured time zone instead of the one used by
your local JVM. I recommend using the UTC time zone.

<persistence>
<persistence-unit name="my-persistence-unit">
...
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.jdbc.time_zone" value="UTC"/>
...
</properties>
</persistence-unit>
</persistence>

When you now rerun the test, you will not see any difference in the
log file.

10:06:41,070 DEBUG SQL:92 - insert into MyEntity (zonedDateTime, id) values


(?, ?)
10:06:41,107 TRACE BasicBinder:65 - binding parameter [1] as [TIMESTAMP] -
[2019-07-18T15:05:30-04:00[UTC-04:00]]
10:06:41,108 TRACE BasicBinder:65 - binding parameter [2] as [BIGINT] - [1]

But if you look at the database table, you can see, that Hibernate now
converted the ZonedDateTime to the UTC time zone.

www.thoughts-on-java.org
Date and Time Mappings with Hibernate and JPA
Conclusion
JPA and Hibernate can map database columns of type DATE, TIME
and TIMESTAMP to various Java classes. You can map them to:

 java.util.Date and java.util.Calendar


 java.sql.Date, java.sql.Time and java.sql.Timestamp
 java.time.LocalDate, java.time.LocalDateTime,
java.time.OffsetTime, java.time.OffsetDateTime,
java.time.ZonedDateTime, java.time.Duration
You just need to decide which Java type you want to use in your
code. I recommend using the classes in the java.time package. They
are part of the Date and Time API, which was introduced in Java 8.
These classes are a lot easier to use in your mapping and your
business code.

www.thoughts-on-java.org
Implementing the Repository pattern
The repository pattern is extremely popular. In its modern
interpretation, it abstracts the data store and enables your business
logic to define read and write operations on a logical level. It does
that by providing a set of methods to read, persist, update and
remove an entity from the underlying data store.

Explaining the repository pattern


The repository pattern is pretty simple. An interface defines the
repository with all logical read and write operations for a specific
entity. You can see an example of such a repository interface in the
diagram.
The interface gets implemented by one or more classes that provide
data store specific implementations of each interface method.

In my experience, it only rarely happens that you need to support


more than one data store. So, you could argue that this pattern
creates an overengineered persistence layer. But the interface
abstraction also enables frameworks to generate huge parts of the
required code.

Implementing the repository pattern


In most enterprise projects, you only need to define the repository
interfaces. Spring Data JPA and Apache DeltaSpike Data can generate
standard repository implementations for you. You just need to
provide your own implementation, if your implementation gets

www.thoughts-on-java.org
Implementing the Repository pattern
especially complex. I will show you more of that in the following
articles of this series.
But for now, let's implement the repository pattern without any
frameworks. That makes the pattern easier to understand and
highlights the benefits of frameworks that generate repetitive parts
of the implementation.

Defining the repository interface


Let's implement the same BookRepository interface as I showed you
in the diagram. It defines 4 methods that you can use to:
 save a new or changed entity (Please keep in mind that
Hibernate detects and persists all changes of managed entities
automatically. So, you don't need to call the save method after
you changed any entity attributes),
 delete an entity,
 find an entity by its primary key and
 find an entity by its title.

public interface BookRepository {

Book getBookById(Long id);

Book getBookByTitle(String title);

Book saveBook(Book b);

void deleteBook(Book b);


}

www.thoughts-on-java.org
Implementing the Repository pattern
Implementing the repository with JPA and Hibernate
In the next step, you can implement the BookRepository interface.

public class BookRepositoryImpl implements BookRepository {

private EntityManager em;

public BookRepositoryImpl(EntityManager em) {


this.em = em;
}

@Override
public Book getBookById(Long id) {
return em.find(Book.class, id);
}

@Override
public Book getBookByTitle(String title) {
TypedQuery<Book> q = em.createQuery("SELECT b FROM Book b
WHERE b.title = :title", Book.class);
q.setParameter("title", title);
return q.getSingleResult();
}

@Override
public Book saveBook(Book b) {
if (b.getId() == null) {
em.persist(b);
} else {
b = em.merge(b);
}
return b;
}

@Override

www.thoughts-on-java.org
public void deleteBook(Book b) {
Implementing the Repository pattern

@Override
public void deleteBook(Book b) {
if (em.contains(b)) {
em.remove(b);
} else {
em.merge(b);
}
}
}

If you ever called a JPQL query or persisted an entity in your business


layer, the code of my repository implementation should look familiar.
There is no big difference between implementing these operations in
your business code or as part of a repository implementation.

In this example, the only noticeable difference is the implementation


of the saveBook(Book b) method. You can call this method to persist a
new entity or to merge an existing one. So, you need to detect if the
method got called with a new or an existing entity. In this example, I
let Hibernate generate the primary key values. So, the id attribute of
all new entities should be null. If it isn't null, it should be an existing
entity which then gets merged into the persistence context.

www.thoughts-on-java.org
Ultimate Guide to JPQL

Selection – The FROM clause


The FROM clause defines from which entities the data gets selected.
Hibernate, or any other JPA implementation, maps the entities to the
according database tables.
The syntax of a JPQL FROM clause is similar to SQL but uses the
entity model instead of table or column names.

SELECT a FROM Author a

Joining multiple entities


Inner Joins
If you want to select data from multiple entities, like all authors and
the books they’ve written, you have to join the entities in the FROM
clause. The easiest way to do that is to use the defined associations
of an entity like in the following code snippet.

SELECT a, b FROM Author a JOIN a.books b

JOINs of unrelated entities are not supported by the JPA


specification, but you can use a theta join which creates a cartesian
product and restricts it in the WHERE clause to the records with
matching foreign and primary keys.

SELECT b, p FROM Book b, Publisher p WHERE


b.fk_publisher = p.id

www.thoughts-on-java.org
Ultimate Guide to JPQL

Left Outer Joins


If you want to include the authors without published books, you have
to use a LEFT JOIN, like in the following code snippet.

SELECT a, b FROM Author a LEFT JOIN a.books b

Additional Join Conditions


Sometimes you only want to join the related entities which fulfill
additional conditions. Since JPA 2.1, you can do this for INNER JOINs
and LEFT JOINs with an additional ON statement

SELECT a, p FROM Author a JOIN a.publications p


ON p.publishingDate > ?1

Path expressions or implicit joins


Path expressions create implicit joins and are one of the benefits
provided by the entity model. You can use the ‘.’ operator to navigate
to related entities like I do in the following code snippet.

SELECT b FROM Book b


WHERE b.publisher.name LIKE ‘%es%

www.thoughts-on-java.org
Ultimate Guide to JPQL
Polymorphism and Downcasting
Polymorphism
When you choose an inheritance strategy that supports polymorphic
queries, your query selects all instances of the specified class and its
subclasses.

SELECT p FROM Publication p

Or you can select a specific subtype of a Publication, like a BlogPost.

SELECT b FROM BlogPost b

Downcasting
Since JPA 2.1, you can also use the TREAT operator for downcasting
in FROM and WHERE clauses. I use that in the following code snippet
to select all Author entities with their related Book entities.

SELECT a, p FROM Author a


JOIN treat (a.publications AS Book) p

www.thoughts-on-java.org
Ultimate Guide to JPQL
Restriction – The WHERE clause
The next important part of a JPQL query is the WHERE clause which
you can use to restrict the selected entities to the ones you need for
your use case. The syntax is very similar to SQL, but JPQL supports
only a small subset of the SQL features. If you need more
sophisticated features for your query, you can use a native SQL
query.
JPQL supports a set of basic operators to define comparison
expressions. Most of them are identical to the comparison operators
supported by SQL and you can combine them with the logical
operators AND, OR and NOT into more complex expressions.

Operators for single-valued expressions


 Equal: author.id = 10
 Not equal: author.id <> 10
 Greater than: author.id > 10
 Greater or equal then: author.id => 10
 Smaller than: author.id < 10
 Smaller or equal then: author.id <= 10
 Between: author.id BETWEEN 5 and 10
 Like: author.firstName LIKE ‘%and%’
The % character represents any character sequence. This
example restricts the query result to all Authors with a
firstName that contains the String ‘and’, like Alexander or
Sandra. You can use an _ instead of % as a single character
wildcard. You can also negate the operator with NOT to exclude
all Authors with a matching firstName.
 Is null: author.firstName IS NULL
You can negate the operator with NOT to restrict the query
result to all Authors who’s firstName IS NOT NULL.
 In: author.firstName IN (‘John’, ‘Jane’)
Restricts the query result to all Authors with the first name
John or Jane.

www.thoughts-on-java.org
Ultimate Guide to JPQL

Operators for collection expressions


 Is empty: author.books IS EMPTY
Restricts the query result to all Authors without associated
Book entities. You can negate the operator (IS NOT EMPTY) to
restrict the query result to all Authors with associated Book
entities.
 Size: size(author.books) > 2
Restricts the query result to all Authors who are associated with
more than 2 Book entities.
 Member of: :myBook member of author.books
Restricts the query result to all Authors who are associated with
a specific Book entity.

Example
You can use one or more of the operators to restrict your query
result. The following query returns all Author entities with a
firstName attribute that contains the String “and” and an id attribute
greater or equal 20 and who have written at least 5 books.

SELECT a FROM Author a


WHERE a.firstName like ‘%and%’
and a.id >= 20
and size(author.books) >= 5

www.thoughts-on-java.org
Ultimate Guide to JPQL

Projection – The SELECT clause


The projection of your query defines which information you want to
retrieve from the database. This part of the query is very different to
SQL. In SQL, you specify a set of database columns and functions as
your projection. You can do the same in JPQL by selecting a set of
entity attributes or functions as scalar values, but you can also define
entities or constructor calls as your projection. Hibernate, or any
other JPA implementation, maps this information to a set of database
columns and function calls to define the projection of the generated
SQL statement.

Entities
Entities are the most common projection in JPQL queries. Hibernate
uses the mapping information of the selected entities to determine
the database columns it has to retrieve from the database. It then
maps each row of the result set to the selected entities.

SELECT a FROM Author a

Scalar values
Scalar value projections are very similar to the projections you know
from SQL. Instead of database columns, you select one or more
entity attributes or the return value of a function call with your
query.

SELECT a.firstName, a.lastName FROM Author a

www.thoughts-on-java.org
Ultimate Guide to JPQL

Constructor references
Constructor references are a good projection for read-only use
cases. They’re more comfortable to use than scalar value projections
and avoid the overhead of managed entities.
JPQL allows you to define a constructor call in the SELECT clause.

SELECT new org.thoughts.on.java.model.AuthorValue


(a.id, a.firstName, a.lastName)
FROM Author a

Distinct query results


You probably know SQL’s DISTINCT operator which removes
duplicates from a projection. JPQL supports this operator as well.

SELECT DISTINCT a.lastName FROM Author a

www.thoughts-on-java.org
Ultimate Guide to JPQL

Functions
Functions are another powerful feature of JPQL that you probably
know from SQL. It allows you to perform basic operations in the
WHERE and SELECT clause. You can use the following functions in
your query:
 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 length): returns a substring of
the given 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 offset): returns the position of
the String search in s. The search starts at the position offset
 abs(Numeric n): returns the absolute value of the given number
 sqrt(Numeric n): returns the square root of the given number
 mod(Numeric dividend, Numeric divisor): returns the
remainder of a division
 treat(x as Type): downcasts x to the given Type
 size(c): returns the size of a given Collection c
 index(orderdCollection): returns the index of the given value in
an ordered Collection

www.thoughts-on-java.org
Ultimate Guide to JPQL

Grouping – The GROUP BY and HAVING clause


When you use aggregate functions, like count(), in your SELECT
clause, you need to reference all entity attributes that are not part of
the function in the GROUP BY clause.
The following code snippet shows an example that uses the
aggregate function count() to count how often each last name occurs
in the Author table.

SELECT a.lastName, COUNT(a)


FROM Author a
GROUP BY a.lastName

The HAVING clause is similar to the WHERE clause and allows you to
define additional restrictions for your query. The main difference is
that the restrictions defined in a HAVING clause are applied to a
group and not to a row.
I use it in the following example to select all last names that start
with a ‘B’ and count how often each of them occurs in the Author
table.

SELECT a.lastName, COUNT(a)


FROM Author a
GROUP BY a.lastName
HAVING a.lastName LIKE ‘B%’

www.thoughts-on-java.org
Ultimate Guide to JPQL

Ordering – The ORDER BY clause


You can define the order in which the database shall return your
query results with an ORDER BY clause. Its definition in JPQL is
similar to SQL. You can provide one or more entity attributes to the
ORDER BY clause and specify an ascending (ASC) or a descending
(DESC) order.

SELECT a FROM Author a


ORDER BY a.lastName ASC, a.firstName DESC

Subselects
A subselect is a query embedded into another query. It’s a powerful
feature you probably know from SQL. Unfortunately, JPQL supports
it only in the WHERE clause and not in the SELECT or FROM clause.
Subqueries can return one or multiple records and can use the
aliases defined in the outer query. The following example shows a
query that uses a subquery to count all Books written by an Author
and returns only the Authors who’ve written more than 1 Book.

SELECT a FROM Author a


WHERE (SELECT count(b) FROM Book b WHERE a
MEMBER OF b.authors ) > 1

www.thoughts-on-java.org
What is Spring Data JPA?
When you implement a new application, you should focus on the
business logic instead of technical complexity and boilerplate code.
That’s why the Java Persistence API (JPA) specification and Spring
Data JPA are extremely popular. JPA handles most of the complexity
of JDBC-based database access and object-relational mappings. On
top of that, Spring Data JPA reduces the amount of boilerplate code
required by JPA. That makes the implementation of your persistence
layer easier and faster.

Spring Data JPA vs JPA vs Hibernate/EclipseLink


JPA is a specification that defines an API for object-relational
mappings and for managing persistent objects. Hibernate and
EclipseLink are 2 popular implementations of this specification (more
details).
Spring Data JPA adds a layer on top of JPA. That means it uses all
features defined by the JPA specification, especially the entity and
association mappings, the entity lifecycle management, and JPA’s query
capabilities. On top of that, Spring Data JPA adds its own features like a
no-code implementation of the repository pattern and the creation of
database queries from method names.

3 reasons to use Spring Data JPA


1. No-code Repositories
Implementing the repository pattern isn’t too complicated but writing
the standard CRUD operations for each entity creates a lot of repetitive
code. Spring Data JPA provides you a set of repository interfaces which
you only need to extend to define a specific repository for one of your
entities.

2. Reduced boilerplate code


Spring Data JPA provides a default implementation for each method
defined by one of its repository interfaces.

3. Generated queries

www.thoughts-on-java.org
What is Spring Data JPA?
Another comfortable feature of Spring Data JPA is the generation of
database queries based on method names. As long as your query isn’t
too complex, you just need to define a method on your repository
interface with a name that starts with find…By. Spring then parses
the method name and creates a query for it.

Using Spring Data JPA with Spring Boot


If youusing Spring Boot and structure your application in the right
way, you only need to add the spring-boot-starter-data-jpa artifact,
and your JDBC driver to your maven build. The Spring Boot Starter
includes all required dependencies and activates the default
configuration.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>

In the next step, you can configure your database connection in the
application.properties or application.yml file. If you use JPA outside
of Spring, you need to configure this and a few other things in the
persistence.xml. Spring Boot and Spring Data JPA handle the default
configuration for you, so that you only need to override the
parameters you want to change.

spring.datasource.url = jdbc:postgresql://localhost:5432/recipes
spring.datasource.username = postgres
spring.datasource.password = postgres

www.thoughts-on-java.org
What is Spring Data JPA?
Repositories in Spring Data JPA
After setting everything up, it’s time to take a closer look at
repositories. There are 3 repository interfaces that you should know
when you use Spring Data JPA:
 CrudRepository
 PagingAndSortingRepository
 JpaRepository
As you might guess from its name, the CrudRepository interface
defines a repository that offers standard create, read, update and
delete operations. The PagingAndSortingRepository extends the
CrudRepository and adds findAll methods that enable you to sort the
result and to retrieve it in a paginated way. Both interface are also
supported by other Spring Data projects, so that you can apply the
same concepts to different datastores. The JpaRepository adds JPA-
specific methods, like flush() to trigger a flush on the persistence
context or findAll(Example<S> example) to find entities by example,
to the PagingAndSortingRepository.

Defining an entity-specific repository


You can use any of the standard interfaces to define your own
repository definition. You, therefore, need to extend one of Spring
Data JPA’s interface, e.g. the CrudRepository interfaces and type it to
the entity class and its primary key class. You can also add custom
query methods to your repository.

public interface BookRepository extends CrudRepository<Book, Long> {

Book findByTitle(String title);


}

www.thoughts-on-java.org
What is Spring Data JPA?
Working with Repositories
After you defined your repository interface, you can use the
@Autowired annotation to inject it into your service implementation.
Spring Data will then provide you with a proxy implementation of
your repository interface. This proxy provides default
implementations for all methods defined in the interface.
In your business code, you can then use the injected repository to
read entities from the database and to persist new or changed
entities.

@RunWith(SpringRunner.class)
@SpringBootTest
public class GettingStartedApplicationTests {

@Autowired
private BookRepository bookRepository;

@Test
@Transactional
public void testByTitle() {
Book b = bookRepository.findByTitle("Hibernate Tips");
Assert.assertEquals(new Long(1), b.getId());
}
}

www.thoughts-on-java.org
JPA FetchTypes
What is a FetchType
The FetchType defines when Hibernate gets the related entities from
the database, and it is one of the crucial elements for a fast
persistence tier.
In general, you want to fetch the entities you use in your business
tier as efficiently as possible. But that’s not that easy. You either get
all relationships with one query or you fetch only the root entity and
initialize the relationships as soon as you need them.

Default FetchTypes
The default depends on the cardinality of the relationship. All to-one
relationships use FetchType.EAGER and all to-many relationships
FetchType.LAZY.

How to change a FetchType


You can change the default FetchType by providing your preferred
FetchType to the relationship annotation as you can see in the
following code snippet.
@Entity
@Table(name = "purchaseOrder")
public class Order implements Serializable {

@OneToMany(mappedBy = "order", fetch = FetchType.EAGER)


private Set<OrderItem> items = new HashSet<OrderItem>();

...

www.thoughts-on-java.org
JPA FetchTypes
FetchType.EAGER – Fetch it so you’ll have it when you need it
The FetchType.EAGER tells Hibernate to get all elements of a
relationship when selecting the root entity.

OrderItem orderItem = em.find(OrderItem.class, 1L);


log.info("Fetched OrderItem: "+orderItem);
Assert.assertNotNull(orderItem.getProduct());

05:41:38,726 DEBUG SQL:92 - select orderitem0_.id as id1_0_0_,


orderitem0_.order_id as order_id4_0_0_, orderitem0_.product_id as
product_5_0_0_, orderitem0_.quantity as quantity2_0_0_,
orderitem0_.version as version3_0_0_, order1_.id as id1_2_1_,
order1_.orderNumber as orderNum2_2_1_, order1_.version as
version3_2_1_, product2_.id as id1_1_2_, product2_.name as
name2_1_2_, product2_.price as price3_1_2_, product2_.version as
version4_1_2_ from OrderItem orderitem0_ left outer join
purchaseOrder order1_ on orderitem0_.order_id=order1_.id left outer
join Product product2_ on orderitem0_.product_id=product2_.id where
orderitem0_.id=?
05:41:38,764 INFO FetchTypes:77 - Fetched OrderItem: OrderItem ,
quantity: 100

This seems to be very useful in the beginning. Joining the required


entities and getting all of them in one query is very efficient.
But keep in mind, that Hibernate will ALWAYS fetch the Product
entity for your OrderItem, even if you don’t use it in your business
code. If the related entity isn’t too big, this is not an issue for to-one
relationships. But it will most likely slow down your application if you
use it for a to-many relationship that you don’t need for your use
case. Hibernate then has to fetch tens or even hundreds of additional
entities which creates a significant overhead.

www.thoughts-on-java.org
JPA FetchTypes
FetchType.LAZY – Fetch it when you need it
The FetchType.LAZY tells Hibernate to only fetch the related entities
from the database when you use the relationship. This is a good idea
in general because there’s no reason to select entities you don’t need
for your uses case.
The used FetchType has no influence on the business code. You can call
the getter method just as any other getter method.

Order newOrder = em.find(Order.class, 1L);


log.info("Fetched Order: "+newOrder);
Assert.assertEquals(2, newOrder.getItems().size());

Hibernate handles the lazy initialization transparently and fetches the


OrderItem entities as soon as the getter method gets called.

05:03:01,504 DEBUG SQL:92 - select order0_.id as id1_2_0_,


order0_.orderNumber as orderNum2_2_0_, order0_.version as
version3_2_0_ from purchaseOrder order0_ where order0_.id=?
05:03:01,545 INFO FetchTypes:45 - Fetched Order: Order
orderNumber: order1
05:03:01,549 DEBUG SQL:92 - select items0_.order_id as
order_id4_0_0_, items0_.id as id1_0_0_, items0_.id as id1_0_1_,
items0_.order_id as order_id4_0_1_, items0_.product_id as
product_5_0_1_, items0_.quantity as quantity2_0_1_, items0_.version
as version3_0_1_, product1_.id as id1_1_2_, product1_.name as
name2_1_2_, product1_.price as price3_1_2_, product1_.version as
version4_1_2_ from OrderItem items0_ left outer join Product
product1_ on items0_.product_id=product1_.id where
items0_.order_id=?

This becomes a performance problem when you use it on a large list of


entities. Hibernate then has to perform an additional SQL statement for
each Order entity to fetch its OrderItems.

www.thoughts-on-java.org

You might also like