If we think of JPA as a database, then we have used the annotations from the last section to define the schema of our sample database. This means they correspond to the part of the SQL that describes the data structure, which is also known as DDL (Data Definition Language). This section is concerned with the processing of data, which means we’ll be dealing with the part of the SQL known by the acronym DML (Data Manipulation Language).
This knowledge is essential for creating the services and methods that create, change or delete instances of entities.
We can use the project from the last chapter. If you have already closed it, you can start another temporary session by clicking the following link. Don’t forget that the whole work is volatile unless you sign up to Codenvy and persist the project in your own workspace.
3.1 Embed EntityManager
To process data, we require an instance of the class EntityManager
. This is usually embedded in a class via dependency injection using the annotation @PersistenceContext
from the package javax.persistence
:
@PersistenceContext private EntityManager em;
For dependency injection in our sample application, however, we are using the CDI annotation @Inject
(see Cloud Tutorial CDI in a Day section 3.2) for each instance. To preserve this convention for the EntityManager
, we require a producer method (see Cloud Tutorial CDI in a Day section 3.4) that makes the EntityManager
available as a CDI bean. Since the EntityManager
is already created using the annotation @PersistenceContext
, it will suffice to annotate the attribute with @Produces
(from package javax.enterprise.inject
) as well. In this case, the instance stored in the attribute will be made available as a CDI bean. Thus, we will simply add the following snippet to the class Resources
from the package press.turngeek.mycampaign.util
, in which our other producer methods are stored:
@Produces @PersistenceContext private EntityManager em;
The addition of this snippet will enable the EntityManager
to be embedded in other CDI beans using the annotation @Inject
. We will do this now for the class CampaignServiceBean
from package press.turngeek.mycampaign.services
:
@Inject EntityManager entityManager;
Once we’ve made this change, it will be possible to access an instance of the class EntityManager
in the CDI bean CampaignServiceBean
using the attribute entityManager
.
3.2 Add a Database Query
Next, we want to extend the method getAllCampaigns
of the CampaignServiceBean
to include a database query. This should cause the method to return all Campaign
objects stored in the database.
Database queries in JPA are executed via so-called NamedQueries
. During this process, the queries are stored directly in the entity and assigned a unique name. This explains how NamedQueries
gots its name.
We now want to add the following query. To help us understand it better, we will write it first in SQL:
SELECT * FROM CAMPAIGN c ORDER BY c.name
This query supplies all rows of the CAMPAIGN
table, sorted according to the name
column. As is usual in SQL, the return value is a relation; however, what we require is a list of complete objects. This means that for the query in JPA, we need a language for executing queries to an object-oriented database. To meet this need, and to guarantee database-independent access to the persistence layer, JPA uses its own language, the JPQL (Java Persistence Query Language). While, luckily, we don’t really have to learn a new language – the expressions are very similar to SQL – readers wishing to learn about JPQL. In our current example, the expression in JPQL is as follows:
SELECT c FROM Campaign c ORDER BY c.name
The only recognisable difference is that the expression in JPQL contains c instead of *
. In SQL, *
returns all columns of the table specified in the FROM
clause (in our case, the table CAMPAIGN
). In JPQL, c
returns the object with the name c
(in our case, the complete Campaign
object). Since the CAMPAIGN
table contains multiple entries, and we are selecting all entries, a list of Campaign
objects is returned – the list of all Campaign
objects. If the command contained a limitation in the form of a WHERE
clause, the expression would only return a subset of the total Campaign
objects.
This JPQL expression now just requires a unique name – we will use Campaign.findAll
. We use the prefix Campaign
to show that the query belongs to the Campaign
entity. The annotation @NamedQuery
(again from the package javax.persistence
) can be used to define a query as follows:
@NamedQuery(name="Campaign.findAll",query="SELECT c FROM Campaign c ORDER BY c.name")
NamedQuery
is usually stored in the class of the affected entity. Thus, in our case, the class Campaign
is expanded as follows (Codenvy might have problems resolving nested annoations. In this case just add the imports manually to the class.):
@NamedQueries({ @NamedQuery(name = Campaign.findAll, query = "SELECT c FROM Campaign c ORDER BY c.name") }) @Entity public class Campaign { public static final String findAll = "Campaign.findAll"; ...
To enable us to obtain type-safe access to the name of the NamedQuery
, a constant, findAll
, is added to store the name.
In the next step, we must expand the getAllCampaigns
method of the CampaignServiceBean
to include the actual database access.
The EntityManager
provides the method createNamedQuery
for accessing a NamedQuery
. It creates a type-safe query obect of the generic type TypedQuery
using the name of the NamedQuery
. We must specify the class of the object to be returned as a second parameter (in our case, Campaign.class
).
When we put all of this together, we get the following expression:
TypedQuery<Campaign> query = entityManager.createNamedQuery(Campaign.findAll, Campaign.class);
The method getResultList
can then be executed on the new TypedQuery
when a list of objects is expected as the result, while getSingleResult
can be used when the query returns only one object. Both methods carry out the actual query and return the corresponding result.
Since we want to return a list of Campaign
objects, our complete method looks as follows:
public List<Campaign> getAllCampaigns() { TypedQuery<Campaign> query = entityManager.createNamedQuery(Campaign.findAll, Campaign.class); List<Campaign> campaigns = query.getResultList(); return campaigns; }
3.3 Executing CRUD Operations
As well as using a query language to obtain database access, we can also use the EntityManager
to carry out the following basic database operations:
- Create new entities (Create)
- Read existing entities (Read)
- Update entitities (Update)
- Delete entities (Delete)
These operations are collectively known as CRUD operations, after the first letter of each of the operation names.
In order to expand the service CampaignService
with these operations, we will now add the method declarations addCampaign
, deleteCampaign
and updateCampaign
to the interface of the service (also CampaignService
).
We do not require a special method to read an existing entity. The existing method CampaignService
– which returns a list of all Campaign
objects via a JPQL query – is sufficient for our needs.
We end up with the code from Listing 3-1 for the interface CampaignService
. Store this in the package press.turngeek.mycampaign.services
.
package press.turngeek.mycampaign.services; import press.turngeek.mycampaign.model.Campaign; import java.util.List; public interface CampaignService { List<Campaign> getAllCampaigns(); void addCampaign(Campaign campaign); void deleteCampaign(Campaign campaign); void updateCampaign(Campaign campaign); }
Listing 3-1 CampaignService interface
The missing methods must now be implemented in the actual service CampaignServiceBean
. We will consider this class alone in the following section, disregarding the MockCampaignServiceBean
mock implementation of the interface, since this is no longer required. You may simply delete this class altogether or fill the missing methods with empty stubs.
We will now implement the method addCampaign
. As described in section 1, the persist method of the class EntityManager
can be used to create a new entity. This means that the implementation of the method addCampaign
looks like this:
public void addCampaign(Campaign campaign) { entityManager.persist(campaign); }
The method updateCampaign
is implemented in a similar way:
public void updateCampaign(Campaign campaign) { entityManager.merge(campaign); }
Here, the method merge is invoked in place of the method persist. Rather than creating a new entity, this updates an existing one. During this process, an object with the same identity is loaded from the database and assumes the value of the passed object. The updated object is then passed to the database[1].
Things become slightly more complex when we come to implement the method deleteCampaign
. While the EntityManager
does have a remove method for deleting entities, this can only be used on instances that are managed by the EntityManager
. This will not necessarily be the case for all possible clients of the service, which will generally use new instances of Campaign
objects that are unknown to the EntityManager
. For this reason, the EntityManager
must begin by finding the Campaign
object in the databse using the identity of the passed Campaign
object. The following lines of code serve to accomplish this task:
Campaign managedCampaign = entityManager.find(Campaign.class, campaign.getId());
The returned Campaign
object retrieves the EntityManager
from the database using the identity campaign.getId()
and manages it from this point onwards. The entity can then be removed from the database using the method remove.
This gives us the following definitions for the method deleteCampaign
:
public void deleteCampaign(Campaign campaign) { Campaign managedCampaign = entityManager.find(Campaign.class, campaign.getId()); entityManager.remove(managedCampaign); }
Managed or not managed?
For newcomers to JPA programming, the process of differentiating between managed and unmanaged instances of entities can initially be very confusing. Generally, managed instances are returned by methods of the class EntityManager
, and some methods expect a managed instance as parameter (the method remove, for example).
Unlike unmanaged instances, managed instances have an active connection to the database. Because of this, managed instances can be used to invoke all methods of the entity, including those that assume access to the database (methods that return assigned entities via a relation, for example). If we tried to invoke such a method on/using an unmanaged entity, we would get a LazyInitializationException
. This will be encountered quite frequently over the remaining part of this chapter.
3.4 Extending CampaignListProducer With Service Delegation
CampaignListProducer
is the bean that manages the lists of the object Campaign
. In order to do this, the bean reacts to events from other beans when a campaign must be added to or deleted from the list.
Once CampaignService
has been extended with the missing methods – add and delete – the CampaignListProducer
will begin delegating to the relevant service methods in order to keep the list of Campaign
objects in the database up to date.
First we will modify CampaignListProducer
’s observer method that responds to the event indicating a Campaign
object should be added. The method at hand is onCampaignAdded
, which now delegates the Campaign
object to be added directly to the method addCampaign
of the CampaignService
:
public void onCampaignAdded(@Observes @Added Campaign campaign) { campaignService.addCampaign(campaign); init(); }
Next, the init method of the CampaignListProducer
is invoked. It updates the list of Campaign
objects stored in the CampaignListProducer
directly from the database.
The event for campaign deletion can be modified in the same way. To achieve this, the method onCampaignDeleted
must be adjusted as follows:
public void onCampaignDeleted(@Observes @Deleted Campaign campaign) { campaignService.deleteCampaign(campaign); init(); }
Up until now, the CampaignListProducer
has not been required to respond to an event if a Campaign
object is updated. The reason for this is that a Campaign
instance exists only once per user – and is in the CampaignListProducer
. Now, however, it exists in both the CampaignListProducer
and the database. For this reason, the database must be informed about all changes made to a Campaign
object.
To accomplish this, we must create the qualifier @Updated
for the campaign update event. We therefore add the following snippet to the class Events
from package press.turngeel.mycampaign.util
:
@Qualifier @Target({ FIELD, PARAMETER }) @Retention(RUNTIME) public @interface Updated { }
This means that the new event can be triggered by EditCampaignController
when the user updates a Campaign
object. To accomplish this, an attribute of the type Event<Campaign>
, annotated with the qualifier @Updated
, must be added to the EditCampaignController
:
@Inject @Updated private Event<Campaign> campaignUpdateEvent;
The event can now be triggered by invoking the fire method of the attribute. To accomplish this, the method doSave
is expanded to include the line
campaignUpdateEvent.fire(campaignProducer.getSelectedCampaign());
This line should be invoked when the user updates a pre-existing Campaign
object.
This means that the method doSave
now contains the following content:
public String doSave() { if (campaignProducer.isAddMode()) { campaignAddEvent.fire( campaignProducer.getSelectedCampaign()); } else { campaignUpdateEvent.fire( campaignProducer.getSelectedCampaign()); } return Pages.LIST_CAMPAIGNS; }
The CampaignListProducer
must now respond to this new event with the qualifier @Updated
. To enable this to happen, the following method, onCampaignUpdated
, is added to the CampaignListProducer
:
public void onCampaignUpdated(@Observes @Updated Campaign campaign) { campaignService.updateCampaign(campaign); init(); }
The event handling occurs in the same way as for other methods. The Campaign
object is delegated to the updateCampaign
method of the CampaignService
and the list of Campaign
objects is reloaded from the databse using the method init
. Unfortunately, Codenvy’s Java editor has problems resolving inner types. Our annotation @Updated
is defined as an inner type of class Events
. Please, add the import statement import press.turngeek.mycampaign.util.Events.Updated;
to the classes CampaignListProducer
and EditCampaignController
by hand.
Service delegation for the CampaignListProducer
bean is now complete. Since the CampaignListProducer
now retrieves the current status of campaigns from the database using the method init and must no longer cache this information, the bean no long requires the SessionScope. Accordingly, this may be now changed to RequestScope. To accomplish this, the annotation @SessionScoped
in the class CampaignListProducer
must be replaced by the annotation @RequestScoped
:
@RequestScoped public class CampaignListProducer { ...
Since, as a result of this change, we are now dealing with a bean with RequestScope, the class is also no longer required to implement Serializable
. Please remove this interface along with the class variable serialVersionUID
.
Once this is done, the implementation of the CampaignListProducer
for this iteration is complete. It should now successfully delegate the task of updating the list of Campaign
objects to the CampaignService
.
Please be patient! The current project is not deployable since we have not yet configured a data base. So if you run the project, you will always get a deployment error! We will solve this problem in the next chapter!
If you have other problems with your project, please compare it with the project behind the following link:
- Strictly speaking, this first happens when the flush method of the
EntityManager
object is invoked; however, this is invoked implicitly by the transaction. ↵