SpringDM笔记21-Using ORM within OSGi with Spring DM
Version
1.Object/relational mapping
(1)JPA CONCEPTS
Although any database-based Java application must rely on JDBC, there’s a structural mismatch
betweenobject-orientedapplications,whicharebasedonobjects,andrelationaldatabases,
which are based on rows, tables, and relations.This structural mismatch causes some issues thatare difficult to resolve when directly using JDBC:
■ Handling specific SQL dialects
■ N+1 selects
■ Object graph handling
■ Object mapping
ORM allows you to manage entities through corresponding tables in the database: that’s the
structural part of ORM. It also has a conversational part, in which the lifecycle of entities is
introduced through several states. Transitions between these states allow the JPA engine to
detect the operations that trigger the persistence context to be flushed or synchronized.
The persistence context contains all the persistence instances currently manipulated by JPA. This
context can be thought of as a first-level cache for the calling code. In enterprise applications, this
context is commonly associated with a transaction or a web request and is flushed at the end of
each.
JPA methods controlling transitions in the mapped object lifecycle:
■ getReference and find:Allow you to get persistent entities from the database. Updates to these entities are automatically synchronized with the database on a flush operation. Related SQL
equests correspond to SELECT requests.
■ persist:Allows you to create persistent entities in order to add them to the database. Related SQL
requests correspond to INSERT requests.
■ merge:Allows you to attach detached entities. This operation results in persistent entities.
Related SQL requests correspond to UPDATE requests.
■ delete:Allows you to remove persistent entities from the database. Related SQL requests
correspond to DELETE requests.
■ flush and commit:Allow you to execute SQL requests corresponding to operations done on the
persistent context. All pending transitions on managed entities, except for loading, are done at
this point.
(2)JAVA PERSISTENCE API
JPA实现:Hibernate JPA,OpenJPA,EclipseLink。
Implementation of the Contact class using JPA annotations for mapping:
@Entity
publicclassContact{
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
privateintid;
@Column(name="last_name")
privateStringlastName;
@Column(name="first_name")
privateStringfirstName;
@OneToMany(mappedBy="contact",fetch=FetchType.EAGER,cascade=CascadeType.ALL)
privateList<Address>addresses;
(...)
}persistence.xml file: (It’s commonly located under the METAINF directory)
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="myUnit" transaction-type="RESOURCE_LOCAL">
<!--exclude-unlisted-classes>true</exclude-unlisted-classes-->
<class>com.manning.sdmia.dataaccess.domain.model.Contact</class>
<class>com.manning.sdmia.dataaccess.domain.model.Address</class>
</persistence-unit>
</persistence>The main abstractions of this API are the EntityManager and EntityManagerFactory interfaces:
■ The EntityManager is associated with a persistence context and provides a set of methods to
interact with it. This interface is responsible for loading data and updating the states of managed
entities. ■ The EntityManagerFactory interface corresponds to the EntityManager’s factory and is in charge
of loading the persistence.xml file as part of its creation.
Using the EntityManager interface:
EntityManagerFactory emf = getEntityManagerFactory();
EntityManagerem=null;
try{
em=emf.createEntityManager();
entityManager.getTransaction().begin();
Contactcontact=newContact();
contact.setLastName("Piper");
contact.setFirstName("Andy");
em.persist(contact);
entityManager.getTransaction().commit();
}catch(Exceptionex){
(...)
entityManager.getTransaction().rollback();
}finally{
closeEntityManager(em);
}Using the EntityManager interface:
EntityManagerFactory emf = getEntityManagerFactory();
EntityManagerem=null;
Try{
em=emf.createEntityManager();
Queryquery=em.createQuery("selectcfromContact");
List<Contact>contacts=query.getResultList();
for(Contactcontact:contacts){
(...)
}
}catch(Exceptionex){
(...)
}finally{
closeEntityManager(em);}
2.Load-time weaving(LTW)
Some JPA implementations have made the choice to use load-time weaving (LTW) to instrument
managed entities. Because LTW is tied to classloading, and LTW isn’t supported by all OSGi
containers.
(1)LOAD-TIME WEAVING CONCEPTS
Load-time weaving (LTW) is related to AOP. Without going into details, the term “weaving” refers
to the way aspects are applied to the application code. The weaving mechanism isn’t
standardized and several approaches exist:
■Compilation-timeweaving—Javacodeisupdatedtoaddaspectsbeforeitscompilation
■Binaryweaving—Updatesbytecodetoaddaspectsafteritscompilation
■ Load-time weaving—Modifies the bytecode of classes as they’re loaded into a JVMLTW updates class content as classes are loaded. Several mechanisms in Java can be used to
implement it:
■ Dedicated classloader—A dedicated classloader is used to update classes as they’re loaded. ■ Java 5 agent—Java 5 introduced the concept of a Java agent, which makes it possible to
instrument classes while loading them. This mechanism is based on the Instrumentation and
ClassFileTransformer interfaces. The latter specifies how to transform classes and must be
registered through the Instrumentation interface. ■ Java 6 class redefinition—Java 6 allows you to redefine classes to update their logic after JVM
startup. In this case, the ClassDefinition interface can be defined and registered through the
Instrumentation interface. ■ Class enhancer tools—Such tools work by putting a proxy in front of instances to add logic
around method execution. Usually these tools create subclasses dynamically. This approach
isn’t transparent to the application because it must use the tools’ API to obtain updated
instances.
(2)Using LTW with the Equinox container and Spring
EQUINOX ASPECTS:
The Equinox Aspects tool is dedicated to leveraging AOP across all present OSGi components within
an Equinox instance, and it enables LTW using the container’s hooks.
Equinox Aspects takes advantage of the extensible nature of the Equinox Framework:the Equinox
Framework adaptor hooks are responsible for handling internal mechanisms like classloading. This
feature allows you to hook into the framework through the hookable adaptor, which allows you to
add extensions to insert additional functionality into the framework itself. For example,
ClassLoadingHook makes possible various classloader extensions, most specifically bytecode
weaving.
These extensions must be defined as fragments for the system bundle org.eclipse.osgi. To enable
their loading, the container must be launched using the osgi.framework.extensions system property.
These fragments aren’t considered regular bundles because they must be discovered before the
platform is launched.
Required components for using Equinox Aspects:
■ org.eclipse.equinox.weaving.hook:Equinox hook implementation
■ org.eclipse.equinox.weaving.aspectj:Bridge between Equinox Aspects and AspectJ
■ org.eclipse.equinox.weaving.caching:Optional component for caching
■ org.eclipse.equinox.weaving.caching.j9:Optional component for caching
■ com.springsource.org.aspectj.runtime:AspectJ runtime component
■ com.springsource.org.aspectj.weaver:AspectJ weaver component
(3)EQUINOX ASPECTS ADAPTER FOR SPRING
An open LTW implementation for Equinox Aspects:
http://forum.springsource.org/showthread.php?t=60253&page=2
Once the org.eclipse.equinox.weaving.springweaver package is imported in the manifest
configuration, along with the other packages required by Equinox Aspects, the implementation can
be configured in a Spring container as shown in the following snippet:
<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver"/>
或者配置成:
<context:load-time-weaver weaver-class="org.eclipse.equinox.weaving.springweaver
.EquinoxAspectsLoadTimeWeaver"/>
3.Provisioning a container for JPA implementations
(1)COMMON SPRING JPA COMPONENTS
PROVISIONING FOR HIBERNATE JPA:
■ Group ID: javax.persistence
Artifact ID:com.springsource.javax.persistence
Version:1.0.0
■ Group ID:org.springframework
Artifact ID:org.springframework.transaction
Version:3.0.0.M1
■ Group ID:org.springframework
Artifact ID:org.springframework.jdbc
Version:3.0.0.M1
■ Group ID:org.springframework
Artifact ID:org.springframework.orm
Version:3.0.0.M1
(2)PROVISIONING FOR HIBERNATE JPA
Hibernate JPA bundles and their dependencies from the SpringSource EBR:
com.springsource.org.hibernate.ejb :3.4.0.GA
com.springsource.org.hibernate.annotations.common :3.3.0.ga
com.springsource.org.hibernate.annotations :3.4.0.GA
com.springsource.org.hibernate :3.3.1.GA
com.springsource.org.apache.commons.beanutils :1.7.0
com.springsource.org.apache.commons.codec :1.3.0
com.springsource.org.apache.commons.collections: 3.2.0
com.springsource.org.apache.commons.digester:1.8.0
com.springsource.org.apache.commons.io :1.4.0
com.springsource.org.apache.commons.lang :2.4.0
com.springsource.org.apache.commons.pool :1.4.0
com.springsource.antlr :2.7.7
com.springsource.javassist :3.3.0.ga
com.springsource.javax.xml.stream :1.0.1
com.springsource.org.dom4j :1.6.1
com.springsource.org.jgroups :2.5.1
com.springsource.org.objectweb.asm :1.5.3
com.springsource.org.objectweb.asm.attrs :1.5.3
com.springsource.javax.transaction :1.1.0
(3)PROVISIONING FOR OPENJPA
OpenJPA bundles and their dependencies from the SpringSource EBR
com.springsource.org.apache.openjpa:1.1.0
com.springsource.org.apache.commons.lang:2.4.0
com.springsource.org.apache.commons.collections:3.2.1
com.springsource.org.apache.commons.pool:1.4.0
com.springsource.org.objectweb.asm:2.2.3
com.springsource.serp:1.13.1
com.springsource.javax.transaction:1.1.0
(4)PROVISIONING FOR ECLIPSELINK
EclipseLink bundles and their dependencies from the SpringSource EBR:
com.springsource.org.eclipse.persistence:1.0.0
com.springsource.org.eclipse.persistence.jpa:1.0.0
com.springsource.org.eclipse.persistence.asm:1.0.0
com.springsource.org.eclipse.persistence.antlr:1.0.0
3.Using JPA in OSGi with Spring DM
(1)CONFIGURING JPA WITH SPRING SUPPORT
Spring’s JPA support provides two classes to configure JPA and its underlying implementation: LocalEntityManagerFactoryBean and LocalContainerEntityManagerFactoryBean. These classes
implement Spring’s FactoryBean interface and allow you to configure a JPA factory of type
EntityManagerFactory.
The LocalEntityManagerFactoryBean class is the simplest way to configure JPA in Spring. It uses
an autodetection mechanism to discover the configured JPA implementation from the persistence.xml
file located in the META-INF directory in the classpath,but it provides no way to link to an existing
JDBC data source configured in Spring.This makes it impossible to use this abstraction in an OSGi
environment, because data sources are registered as OSGi services.
The LocalContainerEntityManagerFactoryBean class is more flexible and particularly suitable when
using JPA with OSGi. It allows you to inject a data source and specify a chosen JPA implementation.
The LocalContainerEntityManagerFactoryBean class is based on implementations of the
JpaVendorAdapter interface. Implementations of this interface can be injected in this class to select
the JPA implementation to use. Built-in implementations of this interface are listed follow:. All these
implementations are located in the org.springframework.orm.jpa.vendor package:
Hibernate JPA:org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
OpenJPA:org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter
EclipseLink:org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter
Configuration of the Hibernate JPA implementation with Spring:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<propertyname="dataSource"ref="dataSource"/>
<propertyname="jpaVendorAdapter"ref="hibernateJpaVendorAdapter"/>
<property name="persistenceXmlLocation"value="classpath:/com/manning/spring/osgi/jpa/domain/dao/impl/persistence.xml"/> </bean>
<bean id="hibernateJpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<propertyname="databasePlatform"value="org.hibernate.dialect.HSQLDialect"/>
<propertyname="showSql"value="true"/>
</bean>注意:The persistenceXmlLocation property is optional. If this property isn’t specified, the
LocalContainerEntityManagerFactoryBean class uses the persistence.xml file under the META-INF
directory.
下面的Package需要Import:
Import-Package: (...)
javax.persistence;version="1.0.0",
org.springframework.beans.factory;version="3.0.0.M1",
org.springframework.core;version="3.0.0.M1",
org.springframework.dao.support;version="3.0.0.M1",
org.springframework.orm.jpa;version="3.0.0.M1",
org.springframework.orm.jpa.support;version="3.0.0.M1",
org.springframework.orm.jpa.vendor;version="3.0.0.M1"(2)USING JPA WITH JPATEMPLATE AND SPRING DM
Spring’s JPA support provides the JpaDaoSupport class as a base class for all DAOs using the JPA
technology. Having specified this class as a superclass for your DAO, you can inject an instance of
EntityManagerFactory into your DAO and access a correctly configured instance of the template
with the getJpaTemplate method.
DAO structure using Spring’s JPA support:
public class ContactsDaoImpl extends JpaDaoSupport implements ContactsDao {
publicContactgetContact(longid){
return (Contact)getJpaTemplate().getReference(id, Contact.class);}
(...)
}<bean id="entityManagerFactory" (...)>
(...)
</bean>
<beanid="contactsDao"
class="com.manning.sdmia.directory.dao.impl.ContactsDaoImpl">
<propertyname="entityManagerFactory"ref="entityManagerFactory"/>
</bean>4. JPA implementation specifics when used with Spring DM
These are the requirements when using JPA in an OSGi environment:
■ Visibility of mapped classes—When configuring JPA implementations at runtime,the mapped
classes must be visible.
■ Automatic discovery of mapped classes—JPA implementations provide support to autodiscover
mapped classes. This feature has implications when used within an OSGi environment.
■ LTW—JPA implementations require or have the option for load-time class instrumentation.
This feature has a big impact on components and the OSGi container itself, especially when
using LTW.
■ Specific OSGi imports—In addition to the common packages described above, packages must be
specified according to the chosen JPA implementation.
(1) GENERAL ISSUES WITH OSGI
You’ll probably recognize by now that the first issue when using JPA in OSGi is a class visibility
one:mapped classes must be visible to the JPA implementation when creating the
EntityManagerFactory entity. Spring DM is smart enough to make these classes visible to
components of the JPA implementation. Thanks to Spring DM’s TCCL support, the mapped
classes only have to be visible to the component creating the EntityManagerFactory (that’s a DAO implementation bundle in most cases).
Another class visibility issue occurs when using classes enhanced (decorated) by the JPA
implementation. JPA implementations tend to transparently instrument classes, and these
“unexpected” classes must also be visible to the implementation as per OSGi visibility rules.
These classes will obviously depend on the chosen JPA implementation.
Moreover, some JPA implementations try to autodetect classes through classpath scanning for
the Entity annotation. In an OSGi environment, this feature can cause exceptions, as shown in
the following snippet:
Caused by: java.io.FileNotFoundException: /home/[...] / com.manning.sdmia.service.jpa.openjpa/bin/ (Is a directory)
This happens because inspecting JAR files in OSGi doesn’t work the same way it does in classic
Java applications. As a matter of fact, this inspecting is based on URLs using the bundlesource
protocol. Because this isn’t handled by all JPA implementations, the JPA implementation may
not be able to browse the contents of a component.
Best practice is to deactivate this feature and to explicitly define which classes must be used
by the JPA implementation. This can be done in the persistence.xml file, as shown in the
following snippet:
<persistence xmlns=http://java.sun.com/xml/ns/persistence version="1.0">
<persistence-unitname="directoryUnit"transaction-type="RESOURCE_LOCAL">
<!--exclude-unlisted-classes>true</exclude-unlisted-classes-->
<class>com.manning.sdmia.dataaccess.domain.model.Contact</class>
</persistence-unit>
</persistence>Forgetting to set the exclude-unlisted-classes parameter to true can cause some JPA
implementations to crash (such as OpenJPA).
A JPQL problem when using Hibernate JPA. Using a request like the one shown in the following
snippet raises a strange ClassCastException:
from com.manning.sdmia.service.jpa.model.Author a left join a.books
The exception it raised was this: java.lang.ClassCastException: [Ljava.lang.Object; can't be cast
to com.manning.sdmia.service.jpa.model.Author
Adding a select clause solves the problem, as shown in following snippet: select a from com.manning.sdmia.service.jpa.model.Author a left join a.books
(2)USING SPRING LTW AND JPA
The JPA specification defines the contract for class transformation through the ClassTransformer
interface. This interface must be implemented by persistence providers that want to transform
entities and managed classes at class load time or at class redefinition time. This feature isn’t
mandatory, and implementations are free to use or ignore it. The following JPA implementations behave differently regarding LTW:
■ Visibility of mapped classes—Use the built-in support provided by Spring DM for making
mapped classes visible when configuring JPA entities at runtime. ■ Automatic discovery of mapped classes—Deactivate the automatic discovery of mapped
classes using the exclude-unlisted-classes tag in the persistence. xml configuration file. This
feature isn’t commonly supported within an OSGi environment. ■ LTW—Configure LTW if needed by the JPA implementation or if you want to use it and your
JPA implementation allows it. In the Equinox container, LTW requires the use of Equinox
Aspects and its integration with Spring’s LTW support. ■ Specific OSGi imports—Specify all the additional packages in the Import-Package header
for the chosen JPA implementation. These packages correspond to hidden classes used at
runtime by implementations.
Spring enables you to integrate ClassTransformer entities into its generic LTW support when their
use is required for a JPA implementation. To use this support, you need to configure the LTW
implementation that you want to use. In our case, that’s Equinox Aspects. This configuration can
either be done globally through the loadtime-weaver facility of Spring’s context XML namespace
or directly on the EntityManagerFactory, as shown in the following snippet:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa
.LocalContainerEntityManagerFactoryBean">
(...)
<propertyname="loadTimeWeaver">
<beanclass="org.eclipse.equinox.weaving.springweaver
.EquinoxAspectsLoadTimeWeaver"/>
</property>
</bean>Remember that you must specify the osgi.framework.extensions system property with the value
org.eclipse.equinox.weaving.hook to enable Equinox Aspects for LTW. Tracing can be activated for
this feature by setting the org.aspectj.osgi.verbose system property to true.
(3)SPECIFIC CONFIGURATION FOR HIBERNATE JPA
The Hibernate JPA implementation doesn’t use LTW to instrument managed entities. Instead,
The Hibernate JPA implementation uses Javassist to manipulate Java bytecode on the fly.
下面的Package需要Import:
Import-Package: (...)
javassist.util.proxy;version="3.3.0.ga",
org.hibernate.proxy;version="3.3.1.GA",
org.hibernate.jdbc;version="3.3.1.GA",
(...)如果使用JPQL,还需要导入:
org.hibernate.hql.ast
This package must be added to the manifest configuration of the bundle whose classloader
corresponds to the context classloader. Because OSGi doesn’t explicitly set the context
classloader, Spring DM’s context classloader support can be used to address this issue by
setting it on the service that exports the DAO entity.
(4)SPECIFIC CONFIGURATION FOR OPENJPA
需要导入下面的Package:
Import-Package: (...)
org.apache.openjpa.enhance;version="1.1.0",
org.apache.openjpa.jdbc.kernel;version="1.1.0"org.apache.openjpa.util;version="1.1.0"