Spring JPA#
What Is The JPA?#
- The
Java Persistence API (JPA)
represents a simplification of the persistence programming model. - Data persistence, the ability to maintain data between application sessions in some form of nonvolatile storage (such as a relational database), is crucial to enterprise applications. Applications that are developed for this environment must either manage data persistence themselves or make use of third-party solutions to handle database updates and retrievals.
JPA
provides a mechanism for managing data persistence and object-relational mapping and functions. JPA
is based on the Java programming model that applies to Java EE environments, but JPA can also function within the Java SE environment. The JPA specification defines the object-relational mapping (ORM) internally, rather than relying on vendor-specific mapping implementations, and uses either annotations or XML to map objects into database tables. By default,JPA
use Hibernate as the implementation for it'sORM
. So we can say that,JPA
defines interfaces forORM
andHibernate
is the implementation of these interfaces.JPA
is designed to operate both inside and outside of a Java Enterprise Edition (Java EE) container. When you run JPA inside a container, applications can use the container to manage the persistence. If there is no container to manage JPA, the application must handle the persistence management itself. Applications that are designed for container managed persistence cannot be used outside a container, while applications that manage their own persistence can function either in a container environment or a Java SE environment.JPA
also provides a query language - JPQL - that you can use to retrieve objects without writing SQL queries specific to the database you are working with.- Java EE containers that support JPA must supply a persistence provider. A JPA persistence provider uses the following elements to persist data in an EJB 3.0 environment:
JPA Elements#
JPA In SpringBoot#
Open Session In View Of JPA#
Example With JPA#
- In this example, we will use JPA in a SpringBoot project to understand, how strong JPA support us to handle data from database.
- So, I advise to read JPA In Spring Boot before getting start with the example.
Dependencies#
- So to use JPA you just simple import some dependencies as below
pom.xml | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
Entity#
- We will create entities which represent for 3 tables in the database.
- Note: You don't need to create these 3 tables in the database because JPA will create for you automatically when the Spring Boot application has started.
- So in Entity classes we will use a lot of mapping annotations from JPA, so you can review these annotations in the table below:
Annotation | Description |
---|---|
@Entity | The @Entity annotation is used to specify that the currently annotated class represents an entity type. Unlike basic and embeddable types, entity types have an identity and their state is managed by the underlying Persistence Context. |
@Table | The @Table annotation is used to specify the primary table of the currently annotated entity. |
@Id | The @Id annotation specifies the entity identifier. An entity must always have an identifier attribute which is used when loading the entity in a given Persistence Context. |
@GeneratedValue | The @GeneratedValue annotation specifies that the entity identifier value is automatically generated using an identity column, a database sequence, or a table generator. Hibernate supports the @GeneratedValue mapping even for UUID identifiers. |
@Type | The @Type annotation is used to specify the Hibernate @Type used by the currently annotated basic attribute. |
@Column | The @Column annotation is used to specify the mapping between a basic entity attribute and the database table column. |
@Enumerated | The @Enumerated annotation is used to specify that an entity attribute represents an enumerated type. In which, ORDINAL: stored according to the enum value’s ordinal position within the enum class, as indicated by java.lang.Enum#ordinal STRING: stored according to the enum value’s name, as indicated by java.lang.Enum#name |
@OneToMany | The @OneToMany annotation is used to specify a one-to-many database relationship. |
@ManyToOne | The @ManyToOne annotation is used to specify a many-to-one database relationship. |
@JoinColumn | The @JoinColumn annotation is used to specify the FOREIGN KEY column used when joining an entity association or an embeddable collection. |
@PrePersist | The @PrePersist annotation is used to specify a callback method that fires before an entity is persisted. |
@PreUpdate | The @PreUpdate annotation is used to specify a callback method that fires before an entity is updated. |
-
The fist Entity is
CustomerEntity
, this Entity will be the parent ofOrderEntity
and oneCustomerEntity
will have manyOrderEntity
. TheCustomerEntity
java class will look like the code below.
CustomerEntity.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
|
- Next we will create
OrderEntity
, so this Entity will be the parent ofItemEntity
and OneOrderEntity
will have manyItemEntity
. TheOrderEntity
java class will be look like as below
OrderEntity.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
|
- Next we will create
ItemEntity
. TheOrderEntity
java class will be look like as below
ItemEntity.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
|
- So that's all for creating Entities and relationships.
Repository#
- Now we will create the
CustomerRepository
which will look like as below
CustomerRepository.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
Service#
- Finally, we will create
CustomerJpaService
which will help us to handle logics for creating customer, orders and items.
CustomerJpaService.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
|
Configuration#
- Now, we will add some configurations into
application.yml
for Datasource and Connection Pool, because Inspring-boot-starter-data-jpa
dependency, we havespring-boot-starter-jdbc
dependency and in JDBC dependency we haveHikariCP
dependency. So we can configureHikariCP
from theapplication.yml
as below
application.yml | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
- For configure DataSource we will focus on properties as below
Configuretion Properties | Description |
---|---|
spring.datasource.driver-class-name | Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. |
spring.datasource.url | JDBC URL of the database. |
spring.datasource.username | Login username of the database. |
spring.datasource.password | Login password of the database. |
- So for congiure
HikariCP
we will focus on some properties as below
Configuretion Properties | Description | Default Value | Sample Configuration Value |
---|---|---|---|
connection-timeout | This property controls the maximum number of milliseconds that a client (that's you) will wait for a connection from the pool. If this time is exceeded without a connection becoming available, a SQLException will be thrown. Lowest acceptable connection timeout is 250 ms. | 30000 | 30000 |
maximum-pool-size | This property controls the maximum size that the pool is allowed to reach, including both idle and in-use connections. Basically this value will determine the maximum number of actual connections to the database backend. A reasonable value for this is best determined by your execution environment. When the pool reaches this size, and no idle connections are available, calls to getConnection() will block for up to connectionTimeout milliseconds before timing out | 10 | 20 |
minimumIdle | This property controls the minimum number of idle connections that HikariCP tries to maintain in the pool. If the idle connections dip below this value and total connections in the pool are less than maximumPoolSize, HikariCP will make a best effort to add additional connections quickly and efficiently. However, for maximum performance and responsiveness to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. | same as maximumPoolSize | 15 |
idleTimeout | This property controls the maximum amount of time that a connection is allowed to sit idle in the pool. This setting only applies when minimumIdle is defined to be less than maximumPoolSize. Idle connections will not be retired once the pool reaches minimumIdle connections. Whether a connection is retired as idle or not is subject to a maximum variation of +30 seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout. A value of 0 means that idle connections are never removed from the pool. The minimum allowed value is 10000ms (10 seconds) | 600000 (10 minutes) | 30000 |
maxLifetime | This property controls the maximum lifetime of a connection in the pool. An in-use connection will never be retired, only when it is closed will it then be removed. On a connection-by-connection basis, minor negative attenuation is applied to avoid mass-extinction in the pool. We strongly recommend setting this value, and it should be several seconds shorter than any database or infrastructure imposed connection time limit. A value of 0 indicates no maximum lifetime (infinite lifetime), subject of course to the idleTimeout setting. The minimum allowed value is 30000ms (30 seconds) | 1800000 (30 minutes) | 180000 |
-
For JPA configuration properties, you can view the table below for more details.
Configuration Properties | Description | Default Value | Sample Configuration Value |
---|---|---|---|
spring.jpa.database-platform | Name of the target database to operate on, auto-detected by default. Can be alternatively set using the "Database" enum. | org.hibernate.dialect.MySQL5InnoDBDialect | |
spring.jpa.generate-ddl | Whether to initialize the schema on startup. | false | true |
spring.jpa.hibernate.ddl-auto | DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Defaults to "create-drop" when using an embedded database and no schema manager was detected. Otherwise, defaults to "none". | "none" | update |
spring.jpa.show-sql | Whether to enable logging of SQL statements | false | true |
spring.jpa.open-in-view | Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request. | true | true |
spring.jpa.properties.* | Additional native properties to set on the JPA provider. |
Testing#
- Now, let's start your Spring Boot service then login into mysql and use command
show processlist;
you will see the result as below
Id | User | Host | db | Command | Time | State | Info |
---|---|---|---|---|---|---|---|
5 | event_scheduler | localhost | NULL | Daemon | 531 | Waiting on empty queue | NULL |
28 | root | localhost | NULL | Query | 0 | init | show processlist |
29 | root | 172.18.0.1:49390 | sample | Sleep | 5 | NULL | |
30 | root | 172.18.0.1:49392 | sample | Sleep | 5 | NULL | |
31 | root | 172.18.0.1:49394 | sample | Sleep | 5 | NULL | |
32 | root | 172.18.0.1:49396 | sample | Sleep | 5 | NULL | |
33 | root | 172.18.0.1:49398 | sample | Sleep | 5 | NULL | |
34 | root | 172.18.0.1:49400 | sample | Sleep | 4 | NULL | |
35 | root | 172.18.0.1:49404 | sample | Sleep | 4 | NULL | |
36 | root | 172.18.0.1:49406 | sample | Sleep | 4 | NULL | |
37 | root | 172.18.0.1:49408 | sample | Sleep | 3 | NULL | |
38 | root | 172.18.0.1:49410 | sample | Sleep | 3 | NULL | |
39 | root | 172.18.0.1:49414 | sample | Sleep | 3 | NULL | |
40 | root | 172.18.0.1:49416 | sample | Sleep | 3 | NULL | |
41 | root | 172.18.0.1:49418 | sample | Sleep | 2 | NULL | |
42 | root | 172.18.0.1:49420 | sample | Sleep | 2 | NULL | |
43 | root | 172.18.0.1:49424 | sample | Sleep | 1 | NULL |
- As you can see, the
HikariCP
configuration has worked, it created 15 connections in the pool of Spring Boot service to database (from Id 29 to Id 43). - Then, if you use command
show tables from <database name>;
(show table in your database), then you can see JPA has created all tables based on entities for you.
1 2 3 4 5 6 7 8 9 |
|
- Moreover, if you use command
select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS c where c.constraint_schema = 'sample';
to check your tables relationships, then you also see JPA craeted all for you.
1 |
|
CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
---|---|---|---|---|---|---|
def | sample | PRIMARY | sample | customers | PRIMARY KEY | YES |
def | sample | UK_rfbvkrffamfql7cjmen8v976v | sample | customers | UNIQUE | YES |
def | sample | PRIMARY | sample | items | PRIMARY KEY | YES |
def | sample | FKirjef006njdi706iiqdfkgk9d | sample | items | FOREIGN KEY | YES |
def | sample | PRIMARY | sample | orders | PRIMARY KEY | YES |
def | sample | FKpxtb8awmi0dk6smoh2vp1litg | sample | orders | FOREIGN KEY | YES |
- Next, Try to add a new customer and call update customer api with orders and items as the requests below and then you will recieve a successful status.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
- If you check the Spring Boot application log, you will see for creating new customer, there are 3 SQL statements have been executed and inserted into
customers
,orders
anditems
tables. For updating customer, it will take 2 SQL statements. Then you can see creating or updating customer took only 1 JDBC connection. It is because by default, JPA is usingopen-in-view=true
, so a transaction will be opened from from the beginning to the end of the HTTP request, if you do many sql statements in a request, so there is only one transaction and the transaction will be committed into database when the request is going to close, so that's why it takes only 1 JDBC connection from the connection pool.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
- Then, check your tables in database, you will there all information have been inserted correctly
1 |
|
id | address | dob | full_name | gender | phone | |
---|---|---|---|---|---|---|
9c107c20-d989-4628-83e1-e728b3d4a5e5 | Binh Duong Province | 1995-10-10 07:00:00 | abc4@gmail.com | Nguyen Minh Duc | M | 0123456789 |
1 |
|
id | created_date | last_updated_date | order_name | order_status | customer_id |
---|---|---|---|---|---|
ea8cc0ce-6232-4ff3-ac91-e8146b6e57ad | 2022-01-29 10:14:24 | 2022-01-29 10:14:24 | PC | CREATED | 9c107c20-d989-4628-83e1-e728b3d4a5e5 |
1 |
|
id | item_name | price | quantity | order_id |
---|---|---|---|---|
5bd4fb0b-a1b0-4e21-becb-5bd2df4982bd | Monitor | 100 | 1 | ea8cc0ce-6232-4ff3-ac91-e8146b6e57ad |
- As you can see, JPA helps you to reduce the workload, you can insert data without writing any SQL native queries.
- Next, we will set
open-in-view=false
then call theupdate customer api
to see what will happen when we update the customer as above.
1 2 3 4 5 6 7 8 9 10 |
|
- Then you check the Spring Boot application log (see the log below), you will see that there are 2 JDBC connections for updating customer api. The first one is used for getting the customer, then the second one is used to update customer. Because
open-in-view=false
so everytime we interact with entity as get or update, so a transaction will be created, commited and closed immediately, In the update customer api, we do two actions: getting a customer and updating it, so JPA made 2 separated transactions and every transaction will took a JDBC connection.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
- Then, check your tables in database, you will there all information have been updated correctly
1 |
|
id | address | dob | full_name | gender | phone | |
---|---|---|---|---|---|---|
9c107c20-d989-4628-83e1-e728b3d4a5e5 | Binh Duong Province | 1995-10-10 07:00:00 | abc5@gmail.com | Nguyen Minh Duc | M | 0123456789 |
See Also#
- Object Relational Mapping
- Hibernate
- JPA In Spring Boot
- Transaction
- Spring Boot Introduction
- Database Configuration In SpringBoot