Tutoriales JPA
Tutoriales JPA
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>
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.
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.
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.
@Entity
The JPA specification requires the @Entity annotation. It identifies a
class as an entity class.
@Entity
public class Author { ... }
Attributes
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
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 {
...
}
Attributes
www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
Specifies the table that contains the mapped
column.
@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.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
...
}
Attributes
GenerationType.SEQUENCE
GenerationType.IDENTITY
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.
@Entity
public class Author {
@Enumerated(EnumType.STRING)
private AuthorStatus status;
...
}
Attributes
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
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
www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
cascade The operations that shall be cascaded to the associated
entities.
@ManyToMany(mappedBy = "authors")
private Set<Book> books;
...
}
@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
@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 {
...
}
www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
Attributes
@OneToOne
One-to-one associations are only rarely used in relational table
models. You can map them using a@OneToOne annotation.
@Entity
public class Manuscript {
@OneToOne(fetch = FetchType.LAZY)
private Book book;
...
}
Attributes
www.thoughts-on-java.org
JPA & Hibernate: Key Annotations
optional Indicates if this association is mandatory
@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.
@Entity
public class Review {
@Id
protected Long id;
…
}
www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
@Entity
public class Review {
@Id
public Long getId() {
return id;
}
…
}
@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.
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;
…
}
www.thoughts-on-java.org
Access Strategies in JPA and Hibernate
@Entity
public class Book {
@ManyToOne
Publisher publisher;
…
}
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.
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;
…
}
@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 {
public AddressKey() {}
@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;
@OneToOne(mappedBy = "address")
private Person person;
...
}
www.thoughts-on-java.org
5 Primary Key Mappings Every Developer Should Know
@Entity
public class Manuscript {
@Id
private Long id;
@OneToOne
@JoinColumn(name = "id")
@MapsId
private Book book;
...
}
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.
www.thoughts-on-java.org
Association Mappings with JPA and Hibernate
@Entity
public class OrderItem {
@ManyToOne
@JoinColumn(name = “fk_order”)
private Order order;
…
}
@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.
@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();
…
}
@Entity
public class Order {
…
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.
@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.
@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>();
…
}
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.
@Entity
public class Customer{
@OneToOne
@JoinColumn(name = “fk_shippingaddress”)
private ShippingAddress shippingAddress;
…
}
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
@Entity
public class PurchaseOrder {
@OneToMany
@JoinColumn(name = "fk_order")
private Set<Item> items = new HashSet<Item>();
...
}
www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings
www.thoughts-on-java.org
Best Practices for Many-To-One and One-To-Many Mappings
em.flush();
em.clear();
@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
@Entity
public class PurchaseOrder {
...
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.
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.
@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>();
...
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.
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.
www.thoughts-on-java.org
The Most Efficient Data Type For To-Many Associations
www.thoughts-on-java.org
The Most Efficient Data Type For To-Many Associations
@Entity
public class Book {
@ManyToMany
@JoinTable(name = "book_author",
joinColumns = { @JoinColumn(name = "fk_book") },
inverseJoinColumns = { @JoinColumn(name = "fk_author") })
private Set authors = new HashSet();
...
}
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
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?
www.thoughts-on-java.org
Composition vs. Inheritance with JPA and Hibernate
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.
...
}
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;
...
}
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.
@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 {…}
@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
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Publication {…}
@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?
www.thoughts-on-java.org
Difference between persist/save & merge/update
www.thoughts-on-java.org
Difference between persist/save & merge/update
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.
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.
www.thoughts-on-java.org
Difference between persist/save & merge/update
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.
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.
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.
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;
...
}
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.
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;
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)));
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.
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.
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.
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:
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.
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.
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.
@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);
}
}
}
www.thoughts-on-java.org
Ultimate Guide to JPQL
www.thoughts-on-java.org
Ultimate Guide to JPQL
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.
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.
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.
www.thoughts-on-java.org
Ultimate Guide to JPQL
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.
www.thoughts-on-java.org
Ultimate Guide to JPQL
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.
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.
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.
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
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.
www.thoughts-on-java.org
Ultimate Guide to JPQL
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.
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.
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.
<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.
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.
...
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.
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.
www.thoughts-on-java.org