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.
JPAprovides a mechanism for managing data persistence and object-relational mapping and functions. JPAis 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,JPAuse Hibernate as the implementation for it'sORM. So we can say that,JPAdefines interfaces forORMandHibernateis the implementation of these interfaces.JPAis 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.JPAalso 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 ofOrderEntityand oneCustomerEntitywill have manyOrderEntity. TheCustomerEntityjava 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 ofItemEntityand OneOrderEntitywill have manyItemEntity. TheOrderEntityjava 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. TheOrderEntityjava 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
CustomerRepositorywhich will look like as below
| CustomerRepository.java | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 | |
Service#
- Finally, we will create
CustomerJpaServicewhich 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.ymlfor Datasource and Connection Pool, because Inspring-boot-starter-data-jpadependency, we havespring-boot-starter-jdbcdependency and in JDBC dependency we haveHikariCPdependency. So we can configureHikariCPfrom theapplication.ymlas 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
HikariCPwe 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
HikariCPconfiguration 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,ordersanditemstables. 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=falsethen call theupdate customer apito 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=falseso 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



