Note
|
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website. |
Learn how to use Java Persistence API (JPA) to access and persist data to a database for your microservices.
Testing change to guide! Another change!
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 Java 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 Java 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 the application server manages the opening and closing of the entity manager for you.
The finish
directory in the root of this guide contains the finished application. Give it a try before you proceed.
Open a command-line session and navigate to the finish/frontendUI
directory.
Run the following Maven goal to build the frontendUI
service and deploy it to Open Liberty:
mvn liberty:run
Open another command-line session and navigate to the finish/backendServices
directory.
Run the following Maven goal to build the backendServices
service and deploy it to Open Liberty:
mvn liberty:run
After you see the following message in both command-line sessions, both your services are ready.
The defaultServer server is ready to run a smarter planet.
Point your browser to the http://localhost:9090/eventmanager.jsf URL. 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 server and then restart it, the events created
are still displayed in the list of current events. Navigate to the finish/backendServices
directory and run the following Maven goals to stop
and then restart the server:
mvn liberty:stop
mvn liberty:run
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 servers 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:
mvn liberty:stop
You need two command-line sessions to start the two services in development mode.
When you run Open Liberty in dev mode, the server listens for file changes and automatically recompiles and deploys your updates whenever you save a new change.
Open a command-line session and navigate to the start/frontendUI
directory. Run the following Maven goal to start the frontendUI
service in dev mode:
mvn liberty:dev
Open a command-line session and navigate to the start/backendServices
directory. Run the following Maven goal to start the backendServices
service in dev mode:
mvn liberty:dev
After you see the following message, your application server in dev mode is ready:
Press the Enter key to run tests on demand.
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 Event
class is a data model that represents events in the event microservice and is annotated with JPA
annotations.
Create theEvent
class.backendServices/src/main/java/io/openliberty/guides/event/models/Event.java
Event.java
link:finish/backendServices/src/main/java/io/openliberty/guides/event/models/Event.java[role=include]
The following table breaks down the new annotations:
Annotation | Description |
---|---|
|
Declares the class as an entity |
|
Specifies details of the table such as name |
|
Specifies a predefined database query that is run by an |
|
Declares the primary key of the entity |
|
Specifies the strategy used for generating the value of the primary key.
The |
|
Specifies that the field is mapped to a column in the database table. The |
The persistence.xml
file is a configuration file that defines a persistence unit. The
persistence unit specifies configuration information for the entity manager.
Create the configuration file.
backendServices/src/main/resources/META-INF/persistence.xml
persistence.xml
link:finish/backendServices/src/main/resources/META-INF/persistence.xml[role=include]
The persistence unit is defined by the <persistence-unit/>
XML element. The 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 transaction-type="JTA"
attribute specifies to use Java Transaction API (JTA) transaction management.
Since we are using a container-managed entity manager, JTA transactions must be used.
A JTA transaction type requires a JTA data source to be provided. The <jta-data-source/>
element specifies the Java Naming and Directory Interface (JNDI) name of
the data source that is used. The data source
has already been configured for you
in the 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
link:finish/backendServices/src/main/liberty/config/server.xml[role=include]
The eclipselink.ddl-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
ddl-generation
properties, see the
JPA Extensions Reference for EclipseLink.
The CRUD operations are defined in the DAO. To perform these operations by using JPA, we need an EventDao
class.
Create theEventDao
class.backendServices/src/main/java/io/openliberty/guides/event/dao/EventDao.java
EventDao.java
link:finish/backendServices/src/main/java/io/openliberty/guides/event/dao/EventDao.java[role=include]
To use the entity manager at runtime, inject it into our CDI bean through the
@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 EventDao
class has a method for each CRUD operation, so let’s break them down:
-
The
createEvent()
method persists an instance of theEvent
entity class to the data store by calling thepersist()
method on anEntityManager
instance. The entity instance becomes managed and changes to it will be tracked by the entity manager. -
The
readEvent()
method returns an instance of theEvent
entity class with the specified primary key by calling thefind()
method on anEntityManager
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
readAllEvents()
method demonstrates an alternative way to retrieve event objects from the database. This method returns a list of instances of theEvent
entity class by using theEvent.findAll
query specified in the@NamedQuery
annotation on theEvent
class. Similarly, thefindEvent()
method uses theEvent.findEvent
named query to find an event with the given name, location and time.
Event.java
link:finish/backendServices/src/main/java/io/openliberty/guides/event/models/Event.java[role=include]
-
The
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 themerge()
method so that changes to loaded fields of the detached entity are tracked. -
The
deleteEvent()
method removes an instance of theEvent
entity class from the database by calling theremove()
method on anEntityManager
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 backendServices/src/main/java/io/openliberty/guides/event/resources/EventResource.java
class and used to access and persist data. The @Transactional
annotation is used in the
EventResource
class to declaratively control the transaction boundaries on the @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
link:finish/backendServices/src/main/java/io/openliberty/guides/event/resources/EventResource.java[role=include]
When the server is running, go to the http://localhost:9090/eventmanager.jsf URL to view the Event Manager application.
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.
Create theEventEntityIT
class.backendServices/src/test/java/it/io/openliberty/guides/event/EventEntityIT.java
EventEntityIT.java
link:finish/backendServices/src/test/java/it/io/openliberty/guides/event/EventEntityIT.java[role=include]
The testInvalidRead()
, testInvalidDelete()
and 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 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 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.
Since 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
.
-------------------------------------------------------
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.EventEntityIT
Results :
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, or by typing q
and then pressing the enter/return
key.
Alternatively, you can run the liberty:stop
goal from the finish
directory in another command-line session for the frontendUI
and backendServices
services:
mvn liberty:stop
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.