While the application middle tier now works as required, it does not observe a few Spring-related best practices.
At the moment, the middle tier does not make any use of transactions. This isn’t a problem while the database access methods are only running single queries, but could lead to problems in the future if the application is made more complex. Thankfully, adding the use of transactions to the middle tier is simple.
Open module-context.xml
in the META-INF/spring
folder of
greenpages.jpa
. Add the following bean definition to create a transaction manager and
associate it with the context’s EntityManager
:
<!-- Transaction manager for a single JPA EntityManagerFactory (alternative to JTA) --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
(Save it, and the greenpages.jpa
module will be refreshed.)
Next, Spring must be told to enable transaction management. In keeping with the use of annotation-based configuration
for the EntityManager
, annotation-based transaction configuration will also be used. Add the following
to enable AspectJ-powered transaction demarcation for appropriately annotated beans:
<!-- Instruct Spring to perform declarative transaction management automatically on annotated classes. --> <tx:annotation-driven mode="aspectj" />
Save the updated file which will trigger (another) successful refresh of greenpages.jpa
.
Lastly, JpaDirectory
needs to be annotated so that it is identified as requiring Spring-based
transaction management. Open JpaDirectory.java
in greenpages.jpa
.
Annotate the class with
@Transactional
and add an
import for org.springframework.transaction.annotation.Transactional
, which Eclipse should suggest:
import org.springframework.transaction.annotation.Transactional; @Transactional final class JpaDirectory implements Directory { …
Save the updated file triggering another successful refresh: JpaDirectory
is now
transactional.
When using JPA, the standard exceptions are somewhat out of keeping with Spring’s exception
model. Spring provides support for automatically translating these exceptions into Spring’s
DataAccessException
hierarchy.
Open module-context.xml
for greenpages.jpa
again and add the
following bean definition to add the exception translator to the application context:
<!-- Post-processor to perform exception translation on @Repository classes (from native exceptions such as JPA PersistenceExceptions to Spring’s DataAccessException hierarchy). --> <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
Save the updated file. The translation will only occur on classes that are annotated with
Spring’s @Repository
stereotype annotation.
JpaDirectory
needs to have this annotation added to it complete the
enabling of the exception translation.
Open JpaDirectory.java
again, annotate the class with
@Repository
and add an import for
org.springframework.stereotype.Repository
:
import org.springframework.stereotype.Repository; @Transactional @Repository final class JpaDirectory implements Directory {
Save the updated file.
At this point the redeploy of the GreenPages application may fail with an error similar to this:
<SPDE0100E> The class with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor', referenced by bean 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0', could not be loaded by class loader 'ServerBundleClassLoader: [bundle=greenpages-1-greenpages.jpa_1.0.0]': …
which indicates that there is some package (org.springframework.dao.annotation
) which is not
available to the “BundleClassLoader
” for bundle greenpages-1-greenpages.jpa_1.0.0
.
We should look in the MANIFEST.MF
file for this bundle, and see that this package is not
imported (in the Import-Package
header). Since Bundlor generated this file (controlled by the
template file template.mf
) we should check that the manifest was re-generated on our last change.
Open template.mf
in greenpages.jpa
and,
in the Overview pane, click on Update MANIFEST.MF in the
Bundle Actions section. The MANIFEST.MF
file is updated,
and the application is redeployed, this time successfully. It might be worthwhile checking the
option Automatically update MANIFEST.MF in the background on
the template.mf
Overview pane.
By default, Bundlor generates Import-Package
entries with no version range
specified. In the absence of a version range, the OSGi default of “any version” is used.
Whilst this is very flexible it’s generally a good idea to restrict an import by
specifying a narrower range. This can be achieved by providing Bundlor with some additional
information in the manifest template.
Open template.mf
for greenpages.jpa
and add the following
Import-Template
header:
Import-Template: org.springframework.*;version="[2.5.6.A,3.0)", greenpages;version="[1.0,1.1)", javax.persistence;version="[1.0.0,1.0.0]"
This header tells Bundlor that all imports of org.springframework
packages
should be in the range 2.5.6.A
inclusive to 3.0
exclusive, that an import of the greenpages
package should be in the
range 1.0
inclusive to 1.1
exclusive, and that an import of
javax.persistence
should be at exactly version 1.0.0
.
Bundlor has also generated an import for the javax.sql
package due to
the greenpages.jpa
module’s use of javax.sql.DataSource
.
This class is provided by the JRE and as such is generally considered to be unversioned, that is it
has the default OSGi version of zero. If version zero is precisely what is required
then add the following to the Import-Template
header:
,javax.sql;version="[0,0]"
but if “any” version is acceptable add the following instead:
,javax.sql;version="0"
Either of these will successfully allow GreenPages to deploy and work correctly. The difference is in the level of flexibility allowed with the external dependency, something which is probably irrelevant in this case, but with other package sources might be important.