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归档注解实体 的实体扫描问题及解决办法