Spring Boot - JPA
Let’s learn how to use Java Persistence API(JPA) to map objects to relational databases using an example project.
Maven dependency
1 | <dependency> |
spring-boot-starter-data-jpa contains the following dependencies:
- Hibernate: One of the most popular JPA implementations.
- Spring ORMs: Core ORM support from the Spring Framework.
- Spring Data JPA: Makes it easy to implement JPA-based repositories.
Datasource
application.properties - configure data source and related settings
1 | # data source |
schema.sql - H2 SQL Script to create Database
1 | CREATE TABLE `user` ( |
data.sql
1 | insert into user(`username`, `password`, `enabled`, `created`) values( 'user', 'pass', true, '2019-01-02'); |
Entity
Traditionally, JPA entities are specified in persistence.xml file. With Spring Boot, you can define entity with annotations. classes annotated with @Entity, @Embeddable or @MappedSuperclass are used as entity.
User Entity
1 |
|
Role Entity
1 |
|
Here we define a Many to Many relationship suing @ManyToMany, @JoinTable and @JoinColumn annotations. User entity owns the relationship be cuase it has @JoinTable annotation.
The Role entity has mappedBy attribute to indicate the relationship is mapped by User entity’s roles collection.
To learn more about creating One-to-One, One-to-Many and Many-to-Many relationship in JPA. see the following post:
- One-to-One Relationship in JPA by baeldung
- Hibernate One to Many Annotation Tutorial by baeldung
- Hibernate Many to Many Annotation Tutorial by Zeger Hendrikse
Solving JSON recursive dependency
When using Jackson to serialize JPA Entities with one-to-many or many-to-many relationship, you can easily get infinite recursion problem because they are bidirection relation. To avoid the error, add @JsonIdentityInfo annotation to tell Jackson the iendity info.
1 |
|
@JsonIdentityInfo
is not the only way to solve this problem, you can also use
@JsonBackReference
and@JsonManagedReference
to handle the relationship@JsonIgnore
or@JsonIgnoreProperties
annotation to ignore the serialization of one or more property.
Refer to this post to see how they are used to handle bidirectional relationship in Json: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
Used with Lombok
Like Json serialization, you can also get stackoverflow when using auto generated methods from Lombok.
It is very common to use lombok to generate a class’s getters, setters etc to reduce boilerplate code. However, the default implementation for toString, equals and hashCode method will cause recursion for a many-to-many relationship. To solve this problem, either stop using @Data annotation to generate code or explicitly define toString, hashCode and equals method.
The User and Role entity class both explicitely define toString, hashCode and equals method to avoid recusion.
JpaRepository
JpaRepository extends CrudRepository and PagingAndSortingRepository. CrudRepository provides methods for generic CRUD operations. PagingAndSortingRepository extends CrudRepository and provides additional methods to retrieve entities using pagination and sorting.
Class extends JpaRepository interface will have all the basic CRUB methods defined and implemented.
You can also add customize methods and queries to the interface. Spring Data will analyze the methods defined by the interfaces and tries to automatically generate queries from the method name.
Use the @Query annotation in Spring Data JPA to execute both JPQL and native SQL queries. For more on JPQL, see
- JPQL – How to Define Queries in JPA and Hibernate
- Video Tutorial - JPA Fundamentals - Lesson 12 - JPQL and using queries
- https://www.baeldung.com/spring-data-jpa-query
For more information on various ways to query in JPA, see Spring Data JPA Query Methods
1 |
|
Using Jpa Entity
You can now autowire the JpaRepository to service class and use it.
1 |
|
About Lazy Loading
You can set the loading strategy by setting fetch attribute. The value can be fetchType.LAZY
or fetchType.EAGER
.
If you encounter LazyInitializationException when using lazy loading, add @Transactional
to the service method because session is needed for lazy loading otherwise a lazyinitializationexception will be thrown.
1 | Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.xinghua24.bookmark.entity.User.roles, could not initialize proxy - no Session |
For more on Lazy loading, see Eager/Lazy Loading In Hibernate by baeldung
Transaction
If we use spring-boot-starter-jdbc or spring-boot-starter-data-jpa, Spring Boot will turn on @EnableTransactionManagement
by default. You can use @Transaction
to declare transaction at class level or method level. @Transaction
by default rolls back on all transactions. You can use rollbackOn and dontRollbackOn attribute to customize the exceptions to rollback.
Service method with @Transactional
annotation
1 |
|
When this method is called, even if the repository saves the change, the exception will cause the transaction to roll back and undo the previous change.
Note that @Transaction should be apply to service class(with @Service annotation). Don’t apply this annotation to multiple layers of the application. Also, the method should be declare public for the transaction to work.
generate-ddl vs ddl-auto
JPA can generate ddl to set up database on startup. There are 2 properties you can set in configuration file.
spring.jpa.generate-ddl
(boolean) switches the feature on and off and is vendor independent.spring.jpa.hibernate.ddl-auto
(enum) is a Hibernate feature that controls the behavior in a more fine-grained way. See below for more detail.
ddl-auto
can take the following options:
- none
- validate - validate only, not changing the schema
- update - update the schema
- create - create the schema
- create-drop - create the schema, drop at the end of the session
ddl-auto
and generate-ddl
should not be used in production.
Reference
- Spring Data JPA Reference
- Baeldung - Introduction to Spring Data JPA by Eugen Paraschiv
- Baeldung - Spring Data JPA @Query
- Baeldung - Transactions with Spring and JPA by Eugen Paraschiv
- Spring boot ddl auto generator
- Spring Boot database initialization
Furthur research on Spring Data, see Baeldung’s Spring Data Topic https://www.baeldung.com/category/persistence/spring-persistence/spring-data/
Source Code: https://github.com/xinghua24/SpringBootExamples/tree/master/jpa