Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/openliberty/guide-jpa-intro
A guide on how to use Java Persistence API to access and persist data to a database for your microservices: https://openliberty.io/guides/jpa-intro.html
https://github.com/openliberty/guide-jpa-intro
Last synced: 3 months ago
JSON representation
A guide on how to use Java Persistence API to access and persist data to a database for your microservices: https://openliberty.io/guides/jpa-intro.html
- Host: GitHub
- URL: https://github.com/openliberty/guide-jpa-intro
- Owner: OpenLiberty
- License: other
- Created: 2018-06-06T20:44:30.000Z (over 6 years ago)
- Default Branch: prod
- Last Pushed: 2024-04-12T20:37:56.000Z (9 months ago)
- Last Synced: 2024-04-13T18:30:07.201Z (9 months ago)
- Language: Java
- Homepage:
- Size: 627 KB
- Stars: 14
- Watchers: 6
- Forks: 16
- Open Issues: 1
-
Metadata Files:
- Readme: README.adoc
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
// Copyright (c) 2018, 2024 IBM Corporation and others.
// Licensed under Creative Commons Attribution-NoDerivatives
// 4.0 International (CC BY-ND 4.0)
// https://creativecommons.org/licenses/by-nd/4.0/
//
// Contributors:
// IBM Corporation
//
:page-layout: guide-multipane
:projectid: jpa-intro
:page-duration: 20 minutes
:page-releasedate: 2018-11-30
:page-description: Learn how to use Java Persistence API (JPA) to access and persist data to a database for your microservices.
:page-tags: ['jakarta-ee']
:page-related-guides: ['cdi-intro']
:page-permalink: /guides/{projectid}
:page-guide-category: none
:guide-author: Open Liberty
:common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod
:source-highlighter: prettify
:page-seo-title: Accessing and persisting data in Java microservices using Java Persistence API (JPA)
:page-seo-description: A getting started tutorial with examples of how to implement a Data Access Object (DAO) to access and persist data to a database in a Java EE or Jakarta EE application using the Java Persistence API (JPA).
= Accessing and persisting data in microservices using Java Persistence API (JPA)[.hidden]
NOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].Learn how to use Java Persistence API (JPA) to access and persist data to a database for your microservices.
:eventapp-url: http://localhost:9090/eventmanager.jsf
:get-events-url: http://localhost:9090/events// =================================================================================================
// Introduction
// =================================================================================================== What you'll learn
You will learn how to use the Java Persistence API (JPA) to map Java objects to relational database tables and perform create, read, update and delete (CRUD) operations on the data in your microservices.
JPA is a Jakarta EE specification for representing relational database table data as Plain Old Java Objects (POJO). JPA simplifies object-relational mapping (ORM) by using annotations to map Java objects to tables in a relational database. In addition to providing an efficient API for performing CRUD operations, JPA also reduces the burden of having to write JDBC and SQL code when performing database operations and takes care of database vendor-specific differences. This capability allows you to focus on the business logic of your application instead of wasting time implementing repetitive CRUD logic.
The application that you will be working with is an event manager, which is composed of a UI and an event microservice for creating, retrieving, updating, and deleting events. In this guide, you will be focused on the event microservice. The event microservice consists of a JPA entity class whose fields will be persisted to a database. The database logic is implemented in a Data Access Object (DAO) to isolate the database operations from the rest of the service. This DAO accesses and persists JPA entities to the database and can be injected and consumed by other components in the microservice. An Embedded Derby database is used as a data store for all the events.
You will use JPA annotations to define an entity class whose fields are persisted to the database. The interaction between your service and the database is mediated by the persistence context that is managed by an entity manager. In a Jakarta EE environment, you can use an application-managed entity manager or a container-managed entity manager. In this guide, you will use a container-managed entity manager that is injected into the DAO so Liberty manages the opening and closing of the entity manager for you.
// =================================================================================================
// Getting Started
// =================================================================================================[role=command]
include::{common-includes}/gitclone.adoc[]=== Try what you'll build
The `finish` directory in the root of this guide contains the finished application. Give it a try before you proceed.
To try out the application, run the following commands to navigate to the `finish/frontendUI` directory and deploy the `frontendUI` service to Open Liberty:
[role="command"]
----
cd finish/frontendUI
mvn liberty:run
----Open another command-line session and run the following commands to navigate to the `finish/backendServices` directory and deploy the service to Open Liberty:
// static guide instruction
ifndef::cloud-hosted[]
[role="command"]
----
cd finish/backendServices
mvn liberty:run
----
endif::[]
// cloud hosted instruction
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-jpa-intro/finish/backendServices
mvn liberty:run
```
endif::[]After you see the following message in both command-line sessions, both your services are ready.
[role="no_copy"]
----
The defaultServer server is ready to run a smarter planet.
----// Static guide instruction
ifndef::cloud-hosted[]
Point your browser to the {eventapp-url} URL to view the Event Manager application.
endif::[]
// Cloud hosted guide instruction
ifdef::cloud-hosted[]
Click the following button to view the Event Manager application:
::startApplication{port="9090" display="external" name="Visit Event Manager application" route="/"}
endif::[]
The event application does not display any events because no events are stored in the database. Go ahead and click `Create Event`, located in the left navigation bar. After entering an event name, location and time, click `Submit` to persist your event entity to the database. The event is now stored in the database and is visible in the list of current events.Notice that if you stop the Open Liberty instance and then restart it, the events created are still displayed in the list of current events. Ensure you are in the `finish/backendServices` directory and run the following Maven goals to stop and then restart the instance:
// static guide instruction
ifndef::cloud-hosted[]
[role=command]
```
mvn liberty:stop
mvn liberty:run
```
endif::[]
// cloud hosted instruction
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-jpa-intro/finish/backendServices
mvn liberty:stop
mvn liberty:run
```
endif::[]The events created are still displayed in the list of current events. The `Update` action link located beside each event allows you to make modifications to the persisted entity and the `Delete` action link allows you to remove entities from the database.
After you are finished checking out the application, stop the Open Liberty instances by pressing `CTRL+C` in the command-line sessions where you ran the `backendServices` and `frontendUI` services. Alternatively, you can run the `liberty:stop` goal from the `finish` directory in another command-line session for the `frontendUI` and `backendServices` services:
// static guide instruction
ifndef::cloud-hosted[]
[role="command"]
----
mvn -pl frontendUI liberty:stop
mvn -pl backendServices liberty:stop
----
endif::[]
// cloud hosted instruction
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-jpa-intro/finish
mvn -pl frontendUI liberty:stop
mvn -pl backendServices liberty:stop
```
endif::[]// =================================================================================================
// Defining a JPA entity class
// =================================================================================================== Defining a JPA entity class
Navigate to the `start` directory to begin.
When you run Open Liberty in https://openliberty.io/docs/latest/development-mode.html[dev mode^], dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change.
Run the following commands to navigate to the `frontendUI` directory and start the `frontendUI` service in dev mode:
// static guide instruction
ifndef::cloud-hosted[]
[role="command"]
----
cd frontendUI
mvn liberty:dev
----
endif::[]
// cloud hosted instruction
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-jpa-intro/start/frontendUI
mvn liberty:dev
```
endif::[]Open another command-line session and run the following commands to navigate to the `backendServices` directory and start the service in dev mode:
// static guide instruction
ifndef::cloud-hosted[]
[role="command"]
----
cd backendServices
mvn liberty:dev
----
endif::[]
// cloud hosted instruction
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-jpa-intro/start/backendServices
mvn liberty:dev
```
endif::[]After you see the following message, your Liberty instance is ready in dev mode:
[role="no_copy"]
----
**************************************************************
* Liberty is running in dev mode.
----Dev mode holds your command line to listen for file changes. Open another command-line session to continue, or open the project in your editor.
To store Java objects in a database, you must define a JPA entity class. A JPA entity is a Java object whose non-transient and non-static fields will be persisted to the database. Any Plain Old Java Object (POJO) class can be designated as a JPA entity. However, the class must be annotated with the `@Entity` annotation, must not be declared final and must have a public or protected non-argument constructor. JPA maps an entity type to a database table and persisted instances will be represented as rows in the table.
The [hotspot=Event]`Event` class is a data model that represents events in the event microservice and is annotated with JPA annotations.
[role="code_command hotspot", subs="quotes"]
----
#Create the `Event` class.#
`backendServices/src/main/java/io/openliberty/guides/event/models/Event.java`
----
Event.java
[source, Java, linenums, role='code_column hide_tags=copyright']
----
include::finish/backendServices/src/main/java/io/openliberty/guides/event/models/Event.java[]
----The following table breaks down the new annotations:
[cols="35, 200", options="header"]
|===
| *Annotation* | *Description*
| [hotspot=Entity]`@Entity` | Declares the class as an entity
| [hotspot=Table]`@Table` | Specifies details of the table such as name
| [hotspot=NamedQuery]`@NamedQuery` | Specifies a predefined database query that is run by an `EntityManager` instance.
| [hotspot=Id]`@Id` | Declares the primary key of the entity
| [hotspot=GeneratedValue]`@GeneratedValue` | Specifies the strategy used for generating the value of the primary key. The `strategy = GenerationType.AUTO` code indicates that the generation strategy is automatically selected
| [hotspot=Column]`@Column` | Specifies that the field is mapped to a column in the database table. The `name` attribute is optional and indicates the name of the column in the table
|===// =================================================================================================
// Configuring JPA
// =================================================================================================== Configuring JPA
The [hotspot file=0]`persistence.xml` file is a configuration file that defines a persistence unit. The persistence unit specifies configuration information for the entity manager.
[role="code_command hotspot", subs="quotes"]
----
#Create the configuration file.#
`backendServices/src/main/resources/META-INF/persistence.xml`
----
persistence.xml
[source, Xml, linenums, role='code_column']
----
include::finish/backendServices/src/main/resources/META-INF/persistence.xml[]
----The persistence unit is defined by the [hotspot=persistence-unit file=0]`persistence-unit` XML element. The [hotspot=transaction-type file=0]`name` attribute is required and is used to identify the persistent unit when using the `@PersistenceContext` annotation to inject the entity manager later in this guide. The [hotspot=transaction-type file=0]`transaction-type="JTA"` attribute specifies to use Java Transaction API (JTA) transaction management. Because of using a container-managed entity manager, JTA transactions must be used.
A JTA transaction type requires a JTA data source to be provided. The [hotspot=jta-data file=0]`jta-data-source` element specifies the Java Naming and Directory Interface (JNDI) name of the data source that is used. The [hotspot=data-source file=1]`data source` has already been configured for you in the [hotspot file=1]`backendServices/src/main/liberty/config/server.xml` file. This data source configuration is where the Java Database Connectivity (JDBC) connection is defined along with some database vendor-specific properties.
server.xml
[source, Xml, linenums, role='code_column']
----
include::finish/backendServices/src/main/liberty/config/server.xml[]
----The [hotspot=schema-generation file=0]`jakarta.persistence.schema-generation` properties are used here so that you aren't required to manually create a database table to run this sample application. To learn more about the JPA schema generation and available properties, see https://jakarta.ee/specifications/persistence/3.0/jakarta-persistence-spec-3.0.html#a12917[Schema Generation, Section 9.4 of the JPA Specification]
// =================================================================================================
// Performing CRUD operations using JPA
// =================================================================================================== Performing CRUD operations using JPA
The CRUD operations are defined in the DAO. To perform these operations by using JPA, you need an [hotspot=EventDao file=0]`EventDao` class.
[role="code_command hotspot", subs="quotes"]
----
#Create the `EventDao` class.#
`backendServices/src/main/java/io/openliberty/guides/event/dao/EventDao.java`
----
EventDao.java
[source, Java, linenums, role='code_column hide_tags=copyright']
----
include::finish/backendServices/src/main/java/io/openliberty/guides/event/dao/EventDao.java[]
----To use the entity manager at runtime, inject it into the CDI bean through the [hotspot=PersistenceContext file=0]`@PersistenceContext` annotation. The entity manager interacts with the persistence context. Every `EntityManager` instance is associated with a persistence context. The persistence context manages a set of entities and is aware of the different states that an entity can have. The persistence context synchronizes with the database when a transaction commits.
The [hotspot=EventDao file=0]`EventDao` class has a method for each CRUD operation, so let's break them down:
* The [hotspot=createEvent file=0]`createEvent()` method persists an instance of the [hotspot=Event file=1]`Event` entity class to the data store by calling the [hotspot=Persist file=0]`persist()` method on an `EntityManager` instance. The entity instance becomes managed and changes to it will be tracked by the entity manager.
* The [hotspot=readEvent file=0]`readEvent()` method returns an instance of the [hotspot=Event file=1]`Event` entity class with the specified primary key by calling the [hotspot=Find file=0]`find()` method on an `EntityManager` instance. If the event instance is found, it is returned in a managed state, but, if the event instance is not found, `null` is returned.
* The [hotspot=readAllEvents file=0]`readAllEvents()` method demonstrates an alternative way to retrieve event objects from the database. This method returns a list of instances of the `Event` entity class by using the `Event.findAll` query specified in the [hotspot=NamedQuery file=1]`@NamedQuery` annotation on the [hotspot=Event file=1]`Event` class. Similarly, the [hotspot=findEvent file=0]`findEvent()` method uses the `Event.findEvent` named query to find an event with the given name, location and time.
Event.java
[source, Java, linenums, role='code_column hide_tags=copyright']
----
include::finish/backendServices/src/main/java/io/openliberty/guides/event/models/Event.java[]
----* The [hotspot=updateEvent file=0]`updateEvent()` method creates a managed instance of a detached entity instance. The entity manager automatically tracks all managed entity objects in its persistence context for changes and synchronizes them with the database. However, if an entity becomes detached, you must merge that entity into the persistence context by calling the [hotspot=Merge file=0]`merge()` method so that changes to loaded fields of the detached entity are tracked.
* The [hotspot=deleteEvent file=0]`deleteEvent()` method removes an instance of the [hotspot=Event file=1]`Event` entity class from the database by calling the [hotspot=Remove file=0]`remove()` method on an `EntityManager` instance. The state of the entity is changed to removed and is removed from the database upon transaction commit.
The DAO is injected into the [hotspot=DAO file=2]`backendServices/src/main/java/io/openliberty/guides/event/resources/EventResource.java` class and used to access and persist data. The [hotspot=Transactional file=2]`@Transactional` annotation is used in the [hotspot=EventResource file=2]`EventResource` class to declaratively control the transaction boundaries on the [hotspot=RequestedScoped file=2]`@RequestScoped` CDI bean. This ensures that the methods run within the boundaries of an active global transaction, which is why it is not necessary to explicitly begin, commit or rollback transactions. At the end of the transactional method invocation, the transaction commits and the persistence context flushes any changes to Event entity instances it is managing to the database.
EventResource.java
[source, Java, linenums, role='code_column hide_tags=copyright']
----
include::finish/backendServices/src/main/java/io/openliberty/guides/event/resources/EventResource.java[]
----// =================================================================================================
// Configuring the Derby driver and the Liberty
// =================================================================================================== Configuring the Derby driver and the Liberty Maven plugin
To use a Derby database, you need to download its libraries and store them to the Liberty shared resources directory. Configure the Liberty Maven plug-in in the `pom.xml` file of the `backendServices` service.
[role="code_command hotspot", subs="quotes"]
----
#Replace the `backendServices/pom.xml` configuration file.#
`backendServices/pom.xml`
----backendServices/pom.xml
[source, xml, linenums, role='code_column']
----
include::finish/backendServices/pom.xml[]
----server.xml
[source, Xml, linenums, role='code_column']
----
include::finish/backendServices/src/main/liberty/config/server.xml[]
----This configuration adds three required Derby dependencies to the [hotspot=dependencies file=0]`dependencies` configuration so Maven can download the Derby libraries locally. The [hotspot=copyDependencies file=0]`copyDependencies` configuration instructs the Liberty Maven plug-in to copy the Derby libraries to the Liberty shared resources directory that is specified through the [hotspot=location file=0]`location` configuration, and is referenced in the [hotspot=shared-dir file=1]`derbyJDBCLib` `library` configuration of the `server.xml` file.
In the terminal where you started the `backendServices` microservice, type `r` and press the `enter/return` key to restart the Liberty instance and pick up the Derby libraries.
// =================================================================================================
// Building and running the application
// =================================================================================================== Running the application
After you see the following message, your Liberty instance is ready in dev mode:
[role="no_copy"]
----
**************************************************************
* Liberty is running in dev mode.
----// Static guide instruction
ifndef::cloud-hosted[]
Go to the {eventapp-url} URL to view the Event Manager application.
endif::[]// Cloud hosted guide instruction
ifdef::cloud-hosted[]
Click the following button to view the Event Manager application:
::startApplication{port="9090" display="external" name="Visit Event Manager application" route="/"}
endif::[]Click `Create Event` in the left navigation bar to create events that are persisted to the database. After you create an event, it is available to view, update, and delete in the `Current Events` section.
// =================================================================================================
// Testing the application
// =================================================================================================== Testing the application
[role="code_command hotspot", subs="quotes"]
----
#Create the `EventEntityIT` class.#
`backendServices/src/test/java/it/io/openliberty/guides/event/EventEntityIT.java`
----
EventEntityIT.java
[source, Java, linenums, role='code_column hide_tags=copyright']
----
include::finish/backendServices/src/test/java/it/io/openliberty/guides/event/EventEntityIT.java[]
----The [hotspot=testInvalidRead]`testInvalidRead()`, [hotspot=testInvalidDelete]`testInvalidDelete()` and [hotspot=testInvalidUpdate]`testInvalidUpdate()` methods use a primary key that is not in the database to test reading, updating and deleting an event that does not exist, respectively.
The [hotspot=testReadIndividualEvent]`testReadIndividualEvent()` method persists a test event to the database and retrieves the event object from the database using the primary key of the entity.
The [hotspot=testCURD]`testCRUD()` method creates a test event and persists it to the database. The event object is then retrieved from the database to verify that the test event was actually persisted. Next, the name, location, and time of the test event are updated. The event object is retrieved from the database to verify that the updated event is stored. Finally, the updated test event is deleted and one final check is done to ensure that the updated test event is no longer stored in the database.
// Include this for info on how to run the tests
=== Running the testsSince you started Open Liberty in dev mode, press the `enter/return` key in the command-line session where you started the `backendServices` service to run the tests for the `backendServices`.
[source, role="no_copy"]
----
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.event.EventEntityIT
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.703 sec - in it.io.openliberty.guides.event.EventEntityITResults :
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
----When you are done checking out the services, exit dev mode by pressing `CTRL+C` in the command-line sessions where you ran the `frontendUI` and `backendServices` services.
// =================================================================================================
// Great work! You're done!
// =================================================================================================== Great work! You're done!
You learned how to map Java objects to database tables by defining a JPA entity class whose instances are represented as rows in the table. You have injected a container-managed entity manager into a DAO and learned how to perform CRUD operations in your microservice in Open Liberty.
include::{common-includes}/attribution.adoc[subs="attributes"]