3 Extending Services to Include Data Access

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.

Start Cloud IDE

My-Campaign after adding JPA Annotations

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:

Start Cloud IDE

My-Campaign after embedding EntityManager in Services


  1. Strictly speaking, this first happens when the flush method of the EntityManager object is invoked; however, this is invoked implicitly by the transaction.