5.3 Building the JPA module

In this section the JPA module in GreenPages is created, building upon an existing skeleton. JPA and its metadata are configured, and a JPA-based Directory service implementation is published which is then consumed by the application’s Web module.

Completing the JPA-based Directory implementation

The greenpages.jpa starter project provides the beginnings of a JPA-based implementation of Directory named JpaDirectory. Import the greenpages.jpa project from the $GREENPAGES_HOME/start directory.

Open the JpaDirectory.java source file in the greenpages.jpa package of greenpages.jpa project (under src/main/java).

The source file contains a Java Persistence Query Language (JPQL) search query that will be used to retrieves listings from the database and empty implementations of the search and findListing methods.

First add an EntityManager to it. Before the new field can be added, EntityManager must be available on the classpath. Open the pom for greenpages.jpa and add the following dependency:

        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>com.springsource.javax.persistence</artifactId>
        </dependency>

Now return to JpaDirectory and add the following field to the class along with an import for javax.persistence.EntityManager (which should be suggested by Eclipse):

    private EntityManager em;

This EntityManager can now be used to implement the search and findListing methods. Update the implementations of these two methods to match the following implementations and then save the updated class:

   public Listing findListing(int id) {
        return em.find(JpaListing.class, id);
    }

    @SuppressWarnings("unchecked")
    public List<Listing> search(String term) {
        return em.createQuery(SEARCH_QUERY).setParameter("term",
                "%" + term.toUpperCase() + "%").getResultList();
    }

(Warnings from Eclipse should now be absent.)

The application context now needs to be updated to create JpaDirectory and to create an EntityManager that can be injected into JpaDirectory.

Open module-context.xml in the META-INF/spring folder of the greenpages.jpa. Add the following beans that will create JpaDirectory and an EntityManager, enable load-time weaving that is required by JPA, and enable annotation-based configuration that will allow the EntityManager to be injected into JpaDirectory:

    <!--
        Activates a load-time weaver for the context. Any bean within the
        context that implements LoadTimeWeaverAware (such as
        LocalContainerEntityManagerFactoryBean) will receive a reference to
        the autodetected load-time weaver.
    -->
    <context:load-time-weaver aspectj-weaving="on" />

    <!-- JPA EntityManagerFactory -->
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        p:dataSource-ref="dataSource">
        <property name="jpaVendorAdapter">
            <bean id="jpaVendorAdapter"
                class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"
                p:databasePlatform="org.eclipse.persistence.platform.database.HSQLPlatform"
                p:showSql="true" />
        </property>
    </bean>

    <!--
        Activates various annotations to be detected in bean classes: Spring's
        @Required and @Autowired, as well as JSR 250's @PostConstruct,
        @PreDestroy and @Resource (if available) and JPA's @PersistenceContext
        and @PersistenceUnit (if available).
    -->
    <context:annotation-config />

    <bean id="directory" class="greenpages.jpa.JpaDirectory" />

The addition of the new beans to the context has introduced a new dependency upon Spring’s ORM support and upon EclipseLink and its JPA implementation. Add the following dependencies to the pom file for greenpages.jpa and save it:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.spring-library</artifactId>
            <type>libd</type>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>com.springsource.org.eclipse.persistence</artifactId>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>com.springsource.org.eclipse.persistence.jpa</artifactId>
        </dependency>

Now switch back to module-context.xml for greenpages.jpa and observe that the errors relating to Spring’s ORM types have now been resolved. Save module-context.xml.

The application context now contains a factory that will create an EntityManager and is configured for annotation-based configuration. The last step in completing JpaDirectory is to annotate the EntityManager field so that Spring will inject the EntityManager created by the factory into the field.

Open JpaDirectory.java again and add an annotation @PersistenceContext to the EntityManager field.

@PersistenceContext
private EntityManager em;

Eclipse will suggest an import for javax.persistence.PersistenceContext; accept this and save the file.

Providing the JPA metadata

JPA uses a file named META-INF/persistence.xml to describe persistence units. persistence.xml refers to a second file, typically named META-INF/orm.xml, to define entity mappings. In the case of GreenPages the persistence.xml file specifies a single persistence unit that points to the greenpages.JpaListing class. The specified mapping file (META-INF/orm.xml) tells the JPA implementation how to map JpaListing to the LISTING database table described above. (For more information on JPA consult the Documentation section in the appendix.)

Create a new file named persistence.xml in the META-INF folder of the greenpages.jpa project. Add the following contents to the new file and then save it:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
                        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">

    <persistence-unit name="GreenPages" transaction-type="RESOURCE_LOCAL">
        <class>greenpages.jpa.JpaListing</class>
    </persistence-unit>

</persistence>

Now create a new file named orm.xml in the same folder as persistence.xml. Add the following contents to the new file and then save it:

<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm 
                        http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
    version="1.0">
    <package>greenpages.jpa</package>
    <entity class="greenpages.jpa.JpaListing" name="Listing">
        <table name="LISTING" />
        <attributes>
            <id name="listingNumber">
                <column name="LISTING_NUMBER" />
                <generated-value strategy="TABLE" />
            </id>
            <basic name="firstName">
                <column name="FIRST_NAME" />
            </basic>
            <basic name="lastName">
                <column name="LAST_NAME" />
            </basic>
            <basic name="emailAddress">
                <column name="EMAIL_ADDRESS" />
            </basic>
        </attributes>
    </entity>
</entity-mappings>

Consuming the DataSource from the service registry

The entityManagerFactory bean that was added earlier depends upon a bean named dataSource which it will use to connect the EntityManager to the GreenPages database. The greenpages.db module already publishes a DataSource to the service registry. greenpages.jpa must now be updated to consume this dataSource.

Open osgi-context.xml in the META-INF/spring folder of the greenpages.jpa project and add the following:

    <!-- import the DataSource from the OSGi service registry -->
    <osgi:reference id="dataSource" interface="javax.sql.DataSource" />

This will result in a bean being created in the application context that is named dataSource. The bean will be of type javax.sql.DataSource and will be backed by a service found in the OSGi service registry that implements the javax.sql.DataSource interface.

Publishing the Directory implementation to the service registry

To make the JPA-based Directory implementation available to GreenPages’ Web module it must be “published” to the OSGi service registry.

Open osgi-context.xml in the META-INF/spring folder of the greenpages.jpa project, add the following and then save the updated file:

    <!--
        export the directory bean to the OSGi service registry under the
        Directory interface
    -->
    <osgi:service ref="directory" interface="greenpages.Directory" />

Generating greenpages.jpa’s manifest using Bundlor

Open the template.mf file in the root of the greenpages.jpa project and switch to the template.mf tab. Add the following entries to the template and save it.

Import-Bundle: com.springsource.org.eclipse.persistence;version="[1.0.0,1.0.0]",
 com.springsource.org.eclipse.persistence.jpa;version="[1.0.0,1.0.0]"
Import-Package: org.springframework.context.weaving;version="[2.5.6.A,3.0.0)",
 org.springframework.transaction.aspectj;version="[2.5.6.A,3.0.0)"
Excluded-Exports: greenpages.jpa

The Excluded-Exports header tells Bundlor that the greenpages.jpa should not be exported from the greenpages.jpa bundle.

The Import-Package entries for org.springframework.context.weaving and org.springframework.transaction.aspectj are needed as Bundlor cannot, yet, detect that these packages are required based on the contents of the bundle’s application context.

Lastly, the Import-Bundle entries for EclipseLink and its JPA implementation are needed as Bundlor cannot, yet, detect that EclipseLink is the JPA implementation that is being used by GreenPages.

Switch to the Overview tab and click Update MANIFEST.MF. As with greenpages.db before, this update will result in some errors being reported in the manifest as the project is not associated with a targetted runtime. Double-click the MANIFEST.MF file in the greenpages project in the Package Explorer. Switch to the Dependencies tab and click Add…. Select greenpages.jpa and click OK. Save the updated file. The problems in the manifest should now be resolved and the GreenPages application should be redeployed due to the addition of the greenpages.jpa module. This redeployment should succeed and it’s now time to try the application again.