1: Persistence Units : Spring 在建立EntityManagerFactory 時讀取persistence.xml, 但是只能有一個, 如果是多個的話有一些解決辦,但不算是完整的,像是here 連結就有一些解法, 不過在OSGi 環境裡面這種方法也有一些問題
2: 多個EntityManagerFactory , 多個Datasource , 每個EntityManagerFactory 又來自不同DataSource :
這種問題目前知道的似乎只有透過JTA來解決? 但是上述的問題又無解, 怎麼在osgi環境裡面mapping又是一個問題,多個Persistence Units 的問題目前似乎無解(我是沒找到目前的解法) , 所以只能預設在我的系統設計暫時只有一個DataSource , 當然我也只有一個EntityManagerFactory , TransactionManager , 這也是方便在OSGi 環境裡使用(多個的話辦別上的問題可能要另解), 所以就有一張不怎麼好看的圖如下:
首先因為環境是在Eclipse Virgo 下, 整合了EclipseLink , 基本的Spring + JPA 設定不了解的可以看看官網的文件 Link , 第一個要解決的問題是如何將每個Entity Class放到獨立的Bundle裡面, 我在每個Entity Bundle裡面的MANIFEST.mf檔加上了自訂的header如下
Meta-Persistence-ScanPackage: com.gfactor.jpa.persistence.test這定義了每個bundle裡面擁有JPA Entity Class的package , 這會在要建立EntityManagerFactory 時被讀取。
整個Project 的Bundle List 如下
如果需要Source Code的話, 可以在我的GitHut找到
OSgi-WebApp-Jpa-EntityClassBundle-
首先是JPA 的Entity Class, 這邊有2個, 分別放在不同的bundle 裡面
plugin-webdomain-jpa-persistence-test , TableInfo.java
package com.gfactor.jpa.persistence.test;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@NamedQuery(name = "QueryByNameAndMail",
query = "SELECT tableinfo FROM TableInfo tableinfo "
"WHERE tableinfo.user_name = :userName "
"and tableinfo.user_mail = :userMail ")
@Entity
@Table(name="tableinfo")
public class TableInfo implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="user_name")
private String user_name;
@Column(name="user_desc")
private String user_desc;
@Column(name="user_mail")
private String user_mail;
public TableInfo(){
}
@Override
public String toString()
{
return "[TableInfo id=" id " user_name=" user_name " user_desc=" user_desc
" user_mail=" user_mail "]";
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getUser_desc() {
return user_desc;
}
public void setUser_desc(String user_desc) {
this.user_desc = user_desc;
}
public String getUser_mail() {
return user_mail;
}
public void setUser_mail(String user_mail) {
this.user_mail = user_mail;
}
}
plugin-webdomain-jpa-persistence-test2 , Bndpageinfo.java
package com.gfactor.jpa.persistence.test2;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@NamedQuery(name = "QueryByName_Ver_Entrypoint",
query = "SELECT bndpage FROM Bndpageinfo bndpage "
"WHERE bndpage.bundle_name = :bndName "
"and bndpage.bundle_version = :bndVer "
"and bndpage.entry_point = :bndEntryPoint")
@Entity
@Table(name="bndpageinfo")
public class Bndpageinfo implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name="bundle_name")
private String bundle_name;
@Column(name="bundle_version")
private String bundle_version;
@Column(name="entry_point")
private String entry_point;
@Column(name="class_name")
private String class_name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getBundle_name() {
return bundle_name;
}
public void setBundle_name(String bundle_name) {
this.bundle_name = bundle_name;
}
public String getBundle_version() {
return bundle_version;
}
public void setBundle_version(String bundle_version) {
this.bundle_version = bundle_version;
}
public String getEntry_point() {
return entry_point;
}
public void setEntry_point(String entry_point) {
this.entry_point = entry_point;
}
public String getClass_name() {
return class_name;
}
public void setClass_name(String class_name) {
this.class_name = class_name;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("print Bndpageinfo object : \n");
sb.append("id : " id "\n");
sb.append("bnudle_name : " bundle_name "\n");
sb.append("bundle_version : " bundle_version "\n");
sb.append("entry_point : " entry_point "\n");
sb.append("class_name : " class_name "\n");
return sb.toString();
}
}
然後最主要的是plugin-webdomain-jpa-core-emf 的部份, 這個bundle 會負責建立EntityManagerFactor和JpaTransactionManager, 但是必須改寫一下才有辦法支援這個部份,首先OsgiPersistenceClassLoader.java, 的部份,這隻負責把所有Entity Class都找出來,回傳一個List 包含全部的Class
package com.gfactor.jpa.internal.loader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.List;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.wiring.BundleWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.osgi.context.BundleContextAware;
import org.springframework.util.ClassUtils;
public class OsgiPersistenceClassLoader implements BundleContextAware{
public static final String JPA_MANIFEST_HEADER = "Meta-Persistence-ScanPackage";
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private BundleContext bundleContext;
public void start(){
getPersistenceList();
}
/**
* Get all Entity class from persistence units bundle ,
* The persistence units bundle have Meta-Persistence-ScanPackage information on the MANIFEST.mf.
* @return List<Class>
*/
public List<Class<?>> getPersistenceList(){
List<Class<?>> clazzList = new ArrayList<Class<?>>();
Bundle[] bundles = bundleContext.getBundles();
try {
clazzList = resolverAllPersistenceUnitsClass(bundles);
} catch (Exception e) {
logger.error("getPersistenceList fail...",e);
}
return clazzList;
}
private List<Class<?>> resolverAllPersistenceUnitsClass(Bundle[] bundles) throws Exception{
List<Class<?>> classList = new ArrayList<Class<?>>();
logger.info("resolverAllPersistenceUnitsClass ......");
for (Bundle bundle : bundles) {
Dictionary<String, String> customJpaMetaInfoHeader =bundle.getHeaders(JPA_MANIFEST_HEADER);
final String allScanPackageListString = customJpaMetaInfoHeader.get(JPA_MANIFEST_HEADER);
if (allScanPackageListString == null)
continue;
logger.info("bundle name = " bundle.getSymbolicName());
logger.info("get JPA_MANIFEST_HEADER String = " allScanPackageListString);
foreachBundleGetPersistenceUnitsClass(classList, bundle,allScanPackageListString);
}
logger.info("all persistence class size = " classList.size());
return classList;
}
private void foreachBundleGetPersistenceUnitsClass(List<Class<?>> classList,
Bundle bundle, final String allScanPackageListString) {
for (String scanClassPath : allScanPackageListString.split(",")) {
BundleWiring wiring = bundle.adapt(BundleWiring.class);
String resourceClassPath = convertClassNameToResourcePath(scanClassPath);
Collection<String> allResourcePathClassName= wiring.listResources(resourceClassPath,"*.class", BundleWiring.LISTRESOURCES_LOCAL);
logger.info("resolverAllPersistenceUnitsClass resolverAllPersistenceUnitsClass : Check BundleWiring = [" wiring "]");
for (String string : allResourcePathClassName) {
String loadClassName = convertResourcePathToClassName(string);
printDebugMessage(wiring, resourceClassPath, allResourcePathClassName,loadClassName);
try {
Class<?> loadClazz = bundle.loadClass(loadClassName);
classList.add(loadClazz);
} catch (ClassNotFoundException e) {
logger.error("can't load class..." , e);
}
}
logger.info("");
}
}
private void printDebugMessage(BundleWiring wiring,
String resourceClassPath, Collection<String> resources,String loadClassName) {
// if(!logger.isDebugEnabled()) return;
logger.info("resolverAllPersistenceUnitsClass resolverAllPersistenceUnitsClass : resourceClassPath = [" resourceClassPath "]");
logger.info("resolverAllPersistenceUnitsClass resolverAllPersistenceUnitsClass : resources Class = [" resources "]");
logger.info("resolverAllPersistenceUnitsClass resolverAllPersistenceUnitsClass : loadClassName = [" loadClassName "]");
logger.info("--");
}
/**
* Convert a "."-based fully qualified class name to a "/"-based resource path.
* @param packageName
* @return String(ResourcePathName)
*/
private String convertClassNameToResourcePath(String packageName){
return ClassUtils.convertClassNameToResourcePath(packageName);
}
/**
* Convert a "/"-based resource path to a "."-based fully qualified class name.
* The param className will replace all ".class" to "" ,
* e.g: com/my/test.class to com.my.test
* @param className
* @return String(className)
*/
private String convertResourcePathToClassName(String className){
String returnClassName = className.replaceAll(".class", "");
returnClassName = ClassUtils.convertResourcePathToClassName(returnClassName);
return returnClassName;
}
@Override
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
}
然後我把原本的LocalContainerEntityManagerFactoryBean做了一些變化,讓scan出來的Class加到persistenceUnitInfo裡面,
LoadPersistenceBundleEntityManagerFactoryBean.java
package com.gfactor.jpa.internal.core;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.jdbc.datasource.lookup.SingleDataSourceLookup;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
import org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo;
import org.springframework.util.ClassUtils;
import com.gfactor.jpa.internal.loader.OsgiPersistenceClassLoader;
/**
* @author momo
*
*/
public class LoadPersistenceBundleEntityManagerFactoryBean extends
AbstractEntityManagerFactoryBean {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private PersistenceUnitManager persistenceUnitManager;
private final DefaultPersistenceUnitManager internalPersistenceUnitManager = new DefaultPersistenceUnitManager();
private PersistenceUnitInfo persistenceUnitInfo;
// private PersistenceProvider persistenceProvider;
private OsgiPersistenceClassLoader osgiPersistenceClassLoader;
// public PersistenceProvider getPersistenceProvider() {
// return persistenceProvider;
// }
//
// public void setPersistenceProvider(PersistenceProvider persistenceProvider) {
// this.persistenceProvider = persistenceProvider;
// }
public void setOsgiPersistenceClassLoader(
OsgiPersistenceClassLoader osgiPersistenceClassLoader) {
this.osgiPersistenceClassLoader = osgiPersistenceClassLoader;
}
public void setPersistenceUnitManager(PersistenceUnitManager persistenceUnitManager) {
this.persistenceUnitManager = persistenceUnitManager;
}
public void setPersistenceXmlLocation(String persistenceXmlLocation) {
this.internalPersistenceUnitManager.setPersistenceXmlLocations(new String[] {persistenceXmlLocation});
}
public void setDataSource(DataSource dataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource));
this.internalPersistenceUnitManager.setDefaultDataSource(dataSource);
}
public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor[] postProcessors) {
this.internalPersistenceUnitManager.setPersistenceUnitPostProcessors(postProcessors);
}
public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
this.internalPersistenceUnitManager.setLoadTimeWeaver(loadTimeWeaver);
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.internalPersistenceUnitManager.setResourceLoader(resourceLoader);
}
/* (non-Javadoc)
* @see org.springframework.orm.jpa.AbstractEntityManagerFactoryBean#createNativeEntityManagerFactory()
*/
@Override
protected EntityManagerFactory createNativeEntityManagerFactory()
throws PersistenceException {
logger.info("createNativeEntityManagerFactory start....");
logger.info("chk ltw instance ..." this.internalPersistenceUnitManager.getLoadTimeWeaver());
PersistenceUnitManager managerToUse = this.persistenceUnitManager;
if (this.persistenceUnitManager == null) {
this.internalPersistenceUnitManager.afterPropertiesSet();
managerToUse = this.internalPersistenceUnitManager;
}
logger.info("managerToUse = " managerToUse);
this.persistenceUnitInfo = determinePersistenceUnitInfo(managerToUse);
JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
if (jpaVendorAdapter != null && this.persistenceUnitInfo instanceof SmartPersistenceUnitInfo) {
((SmartPersistenceUnitInfo) this.persistenceUnitInfo).setPersistenceProviderPackageName(
jpaVendorAdapter.getPersistenceProviderRootPackage());
}
logger.info("this.persistenceUnitInfo getPersistenceUnitName = " this.persistenceUnitInfo.getPersistenceUnitName());
// PersistenceProvider provider = getPersistenceProvider();
PersistenceProvider provider = getPersistenceProvider();
if (provider == null) {
String providerClassName = this.persistenceUnitInfo.getPersistenceProviderClassName();
if (providerClassName == null) {
throw new IllegalArgumentException(
"No PersistenceProvider specified in EntityManagerFactory configuration, "
"and chosen PersistenceUnitInfo does not specify a provider class name either");
}
Class<?> providerClass = ClassUtils.resolveClassName(providerClassName, getBeanClassLoader());
provider = (PersistenceProvider) BeanUtils.instantiateClass(providerClass);
}
if (provider == null) {
throw new IllegalStateException("Unable to determine persistence provider. "
"Please check configuration of " getClass().getName() "; "
"ideally specify the appropriate JpaVendorAdapter class for this provider.");
}
logger.info("provider = " provider.toString());
logger.info("provider.getProviderUtil = " provider.getProviderUtil());
logger.info("provider getClass = " provider.getClass());
logger.info("provider getClassLoader= " provider.getClass().getClassLoader());
List<Class<?>> persistenceClassList = osgiPersistenceClassLoader.getPersistenceList();
scanEntitys(persistenceClassList);
logger.info("persistenceClassList list size = " persistenceClassList);
if (logger.isInfoEnabled()) {
logger.info("Building JPA container EntityManagerFactory for persistence unit '"
this.persistenceUnitInfo.getPersistenceUnitName() "'");
}
this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(this.persistenceUnitInfo, getJpaPropertyMap());
postProcessEntityManagerFactory(this.nativeEntityManagerFactory, this.persistenceUnitInfo);
logger.info("nativeEntityManagerFactory = " nativeEntityManagerFactory);
// EntityManager em = this.nativeEntityManagerFactory.createEntityManager();
// logger.info("em = " em);
return this.nativeEntityManagerFactory;
}
private void scanEntitys(List<Class<?>> classList) {
for (Class clazz : classList) {
logger.info("class name = " clazz.getName());
persistenceUnitInfo.getManagedClassNames().add(clazz.getName());
}
List<String> managedClass = persistenceUnitInfo.getManagedClassNames();
logger.info("managedClass = " managedClass);
List<String> mappingFiles = persistenceUnitInfo.getMappingFileNames();
logger.info("mappingFiles = " mappingFiles);
// String[] pgs = StringUtils.commaDelimitedListToStringArray(scanPackages);
// if (pgs.length > -1) {
// ClassPathScaner p = new ClassPathScaner();
// // p.addIncludeFilter(new AssignableTypeFilter(TypeFilter.class));
// // Set<MetadataReader> bd = p.findCandidateClasss("org.springframework");
// p.addIncludeFilter(new AnnotationTypeFilter(Entity.class));
// Set<MetadataReader> bd = p.findCandidateClasss(pgs);
// List<String> managedClass = persistenceUnitInfo.getManagedClassNames();
// for (MetadataReader b : bd) {
// if (!(managedClass.contains(b.getClassMetadata().getClassName()))) {
// managedClass.add(b.getClassMetadata().getClassName());
// }
// }
// }
}
protected PersistenceUnitInfo determinePersistenceUnitInfo(PersistenceUnitManager persistenceUnitManager) {
if (getPersistenceUnitName() != null) {
return persistenceUnitManager.obtainPersistenceUnitInfo(getPersistenceUnitName());
}
else {
return persistenceUnitManager.obtainDefaultPersistenceUnitInfo();
}
}
protected void postProcessEntityManagerFactory(EntityManagerFactory emf, PersistenceUnitInfo pui) {
}
@Override
public PersistenceUnitInfo getPersistenceUnitInfo() {
return this.persistenceUnitInfo;
}
@Override
public String getPersistenceUnitName() {
if (this.persistenceUnitInfo != null) {
return this.persistenceUnitInfo.getPersistenceUnitName();
}
return super.getPersistenceUnitName();
}
@Override
public DataSource getDataSource() {
if (this.persistenceUnitInfo != null) {
return this.persistenceUnitInfo.getNonJtaDataSource();
}
return this.internalPersistenceUnitManager.getDefaultDataSource();
}
}
這樣取代之後,在使用上會發生一些問題,最常見到的問題就是『Cannot apply class transformer without loadtimeweaver specified』, , 弄了半天都是Load-Time-Weaving 的問題,原本應該設定spring-agent的部份,然後再指定weaving class, 但是在Eclipse Virgo裡面後來有查到,在這邊能看到討論 How do you set up Spring AOP/AspectJ/Virgo , 裡面有提到在Eclipse Virgo裡面當你在xml設定如下
<context:load-time-weaver aspectj-weaving="on"/>kernel會把Spring的LoadTimeWeaver 取代成KernelLoadTimeWeaver, 如果只設定這樣也是不能work , LoadPersistenceBundleEntityManagerFactoryBean裡面會找不到weaver class, 所以完整的XMl設定如下
這邊要把loadTimeWeaver指向"loadTimeWeaver"這個bean(因為LTW 打開會自動instance)
bundle-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- regular spring configuration file defining the beans for this bundle.
The configuration of OSGi definitions is kept in a separate configuration
file so that this file can easily be used for integration testing outside
of an OSGi environment -->
<context:load-time-weaver aspectj-weaving="on" />
<!-- <context:load-time-weaver aspectj-weaving="on" weaver-class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver"/> -->
<context:annotation-config />
<!-- -->
<tx:annotation-driven mode="aspectj" />
<bean id="osgiPersistenceClassLoader"
class="com.gfactor.jpa.internal.loader.OsgiPersistenceClassLoader" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"
/> -->
<!-- Transaction manager for a single JPA EntityManagerFactory (alternative
to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<!-- <bean id="defaultPuMgr" class="com.gfactor.jpa.internal.core.MergingPersistenceUnitManager">
<property name="defaultDataSource" ref="dataSource"/> <property name="osgiPersistenceClassLoader"
ref="osgiPersistenceClassLoader"/> <property name="persistenceXmlLocations">
<list> <value>classpath:META-INF/persistence*.xml</value> </list> </property>
</bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="defaultPuMgr"/> </bean> -->
<bean id="entityManagerFactory"
class="com.gfactor.jpa.internal.core.LoadPersistenceBundleEntityManagerFactoryBean"
p:dataSource-ref="dataSource">
<property name="persistenceUnitName" value="plugin-web-domain"></property>
<property name="osgiPersistenceClassLoader" ref="osgiPersistenceClassLoader"></property>
<property name="jpaVendorAdapter">
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"
p:databasePlatform="org.eclipse.persistence.platform.database.MySQLPlatform"
p:showSql="true" />
</property>
<property name="loadTimeWeaver" ref="loadTimeWeaver"/>
<property name="jpaProperties">
<bean id="jpaProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="eclipselink.weaving">false</prop>
</props>
</property>
</bean>
</property>
</bean>
</beans>
還有osgi 的xml 設定
bundle-context-osgi.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<!-- definitions using elements of the osgi namespace can be included
in this file. There is no requirement to keep these definitions
in a separate file if you do not want to. The rationale for
keeping these definitions separate is to facilitate integration
testing of the bundle outside of an OSGi container -->
<osgi:reference id="dataSource" interface="javax.sql.DataSource" />
<!--
<osgi:reference id="persistenceProvider" interface="javax.persistence.spi.PersistenceProvider" />
-->
<osgi:service ref="entityManagerFactory" interface="javax.persistence.EntityManagerFactory" />
<osgi:service ref="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
</beans>
然後再使用plugin-webdomain-service-test , 來測試bundle , 這時又會出現『 unknown entity type』 的問題, 所以在eclipselink.weaving 是設為false的,但是重要的還是MANIFEST.mf檔的設定,最後為了解決上面的問題我使用了DynamicImport-Package: * , 才解決上面的問題。
plugin-webdomain-jpa-core-emf 的 MANIFEST.mf
Manifest-Version: 1.0 Unversioned-Imports: * Build-Jdk: 1.6.0_26 Built-By: momo Bundle-Version: 1.0.0 Tool: Bnd-1.43.0 Bnd-LastModified: 1313477275968 Bundle-Name: Spring OSGi Bundle for jpa EMF service Bundle-ManifestVersion: 2 Created-By: Apache Maven Bundle Plugin Bundle-SymbolicName: plugin-webdomain-jpa-core-emf Import-Bundle: org.eclipse.persistence.jpa;version="[2.0.0,2.4.0]", org.eclipse.persistence.core;version="[2.0.0,2.4.0]" Import-Package: javax.persistence, javax.persistence.spi, javax.sql, org.eclipse.persistence.logging, org.eclipse.persistence.config, org.osgi.framework, org.osgi.framework.wiring, org.slf4j, org.springframework.core, org.springframework.beans, org.springframework.beans.factory, org.springframework.beans.factory.support, org.springframework.beans.factory.config, org.springframework.dao.support, org.springframework.core.io, org.springframework.instrument.classloading, org.springframework.jdbc.datasource.lookup, org.springframework.orm.jpa, org.springframework.orm.jpa.persistenceunit, org.springframework.context.weaving, org.springframework.transaction.aspectj, org.springframework.transaction.interceptor, org.springframework.orm.jpa.support, org.springframework.orm.jpa.vendor, org.springframework.osgi.context, org.springframework.transaction, org.springframework.transaction.support, org.springframework.util, org.aspectj.weaver, org.aspectj.runtime, org.aspectj.lang, org.aspectj.runtime.reflect DynamicImport-Package: *
Reference :
Eclipse Community Forums : How do you set up Spring AOP/AspectJ/Virgo
Spring Forums : Transactions with aspectj on Virgo
EclipseLink Wiki
非J2EE 容器环境下Spring +JPA 多持久化单元/多个JAR归档注解实体 的实体扫描问题及解决办法

