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.
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.
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>
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.
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" />
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.