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 to access and persist data to a database for your microservices.
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 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 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. The application uses an Embedded Derby database as a datastore 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 application and the database is mediated by the persistence context that is managed by an EntityManager. The EntityManager can be application-managed or container-managed. In this guide you will be using a container-managed EntityManager, placing the burden of managing the lifecycle of the EntityManager on the application server. The lifecycle of the persistence context can either be transaction-scoped or extended when using a container-managed EntityManager. You will be focused on using a container-managed EntityManager with a transaction-scoped persistence context to take advantage of the application server’s services.
Point your browser to the http://localhost:9080/eventmanager.jsf URL. The event application
currently has no events stored in the database. Go ahead and click on the Create Event
button
located in the left navigation bar. After entering an event name, location and time, click on 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:
mvn liberty:stop-server
and then restart it:
mvn liberty:start-server
The events created are still displayed in the list of current events. The Update
button located
beside each event allows you to make modifications to the persisted entity and the Delete
button
allows you to remove entities from the database.
Navigate to the start
directory to begin.
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 model that represents events in the application and is annotated with JPA
annotations. Create the Event
class in the src/main/java/io/openliberty/guides/models/Event.java
file:
link:finish/src/main/java/io/openliberty/guides/models/Event.java[role=include]
Let’s break down the new annotations that are used in this JPA entity class:
Annotation | Description |
---|---|
|
Declares the class as an entity |
|
Specifies details of the table such as name |
|
Specfies a predefined database query that can be executed by the EntityManager at runtime |
|
Declares the pimary key of the entity |
|
Specifies the generation strategy for the value of the primary key.
|
|
Specifies that the field is mapped to a column in the database table, |
An EntityManager provides the functionality to perform operations on the database such as persisting
new entities, fetching entities through the find()
method and queries, updating changes, and
removing entities.
The EntityManager manages the lifecycle of entity instances. 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 configuration information of the EntityManager, such as the transaction type and
datastore, is also defined by the persistence unit specified in the persistence.xml
file.
Create the src/main/resources/META-INF/persistence.xml
file:
link:finish/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. The transaction-type=”JTA”
element specifies
that the persistence context will enlist with Java Transaction API (JTA) global transactions. Since
we are using a container-managed EntityManager, it is required that JTA transactions are used.
The eclipselink.ddl-generation
properties are used here to avoid having 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.
A JTA transaction type requires a data source to be provided. This is done through the
eventjpadatasource
element which specifies the Java Naming and Directory Interface (JNDI) name of
the data source that is to be used.
The Java Database Connectivity (JDBC) connection is specified in the data source which is configured
in the server.xml
file. Create the src/main/liberty/config/server.xml
file:
link:finish/src/main/liberty/config/server.xml[role=include]
The derbyJDBCLib
element is a shared library that points to the directory of the JDBC driver. The
JDBC driver is what enables the application to interact with the database.
The data source configuration is done within the <dataSource>
element. The <properties.derby.embedded>
attribute within the data source configuration contains embedded derby properties such as the database name
and whether to create the database if it does not already exist.
Every entity object has a lifecycle that consists of four states: new, managed, detached and removed.
Whenever a new Event object is created its state will be new, meaning that it is not mapped to a row in
the database table and is not associated with an EntityManger. Managed entities are actively tracked
by the persistence context. Entities become managed when they are persisted, merged, or fetched by
find()
or a query. Detached entities are no longer managed by a persistence context. Entities become
detached when the EntityManager is cleared, closed or explicitly detached. When using a container-managed
EntityManager, entities automatically become detached at the end of transaction boundaries. Lastly,
removed entities will be marked for removal and will be deleted from the database upon transaction commit.
To perform CRUD operations using JPA, create the src/main/java/io/openliberty/guides/dao/EventDao.java
file:
link:finish/src/main/java/io/openliberty/guides/dao/EventDao.java[role=include]
The EventDao
class can then be injected into other components of your application. Notice how
it is injected into the src/main/java/io/openliberty/resources/EventService.java
file to provide
the EventService
class with a way to persist and access data.
In order to use the EntityManager at runtime, it must be injected into our CDI bean through the
@PersistenceContext
annotation. The @Transactional
annotation allows the application to manage
transaction boundaries on CDI managed beans. This ensures that the required methods are executed within
the boundaries of an active global transaction, which is why it is not necessary to explicitly begin,
commit or rollback transactions.
Since we are using a transaction-scoped persistence context, a new persistence context will begin when the EntityManager is invoked within an active transaction and will end when the transaction commits or rolls back. At the end of the transactional method invocation, the persistence context will be notified that the transaction is committing, and will flush any changes made to entity instances it is managing to the database. The managed entities will then become detached.
The EventDao.java
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 datastore by calling thepersist()
method on an EntityManager instance. The event objects state will be changed from new to managed and changes to it are now tracked by the EntityManager. -
The
readEvent()
method will return 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 will be returned in a managed state, but, if the event instance is not found,Null
will be returned. -
The
readAllEvents()
method demonstrates an alternate way to retrieve event objects from the database. This method returns a list of instances of theEvent
entity class using a query specified in the@NamedQuery
annotation on theEvent
class. The event instances will be returned in a managed state. -
The
updateEvent()
method creates a managed instance of a detached entity object. The EntityManager automatically tracks all managed entity objects in its persistence context for changes and synchronizes them with the database. But, if an entity becomes detached, you must merge that entity into the persistence context by calling themerge()
method on anEntityManager
instance in order for changes to the detached entity’s loaded fields to be tracked. -
The
deleteEvent()
method removes an instance of theEvent
entity class from the database by calling theremove()
method on anEntityManager
instance. The managed entity gets removed from the database and its state becomes removed.
When the server is running, visit the http://localhost:9080/eventmanager.jsf URL to view the Event Manager application.
Using the Create event
button in the left navigation bar you are able to create events that will
be persisted to the database. Once you have created an event, it will be available to view, update,
and delete in the Event list
section.
Create the EventEntityTest
test class in the src/test/java/it/io/openliberty/guides/event/EventEntityTest.java
file:
link:finish/src/test/java/it/io/openliberty/guides/event/EventEntityTest.java[role=include]
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 test event’s
name, location, and time is 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 the updated test event is no longer stored in the database.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.jpaguide.tests.EventEntityTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.9 sec - in it.io.openliberty.guides.jpaguide.tests.EventEntityTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
You have 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 have learned how to configure an EntityManager and use container-managed transactions to perform basic CRUD operations on a database using JPA.