拙作R42FWではEclipseLinkとSpring2.5を組み合わせて使用していますが、
この組み合わせで、JPAのLazy Loadingが動作させることができたので、その手順を紹介します。
EclipseLink単品で使用する場合の手順では動かない
まず最初にこちらに書いてある手順でやってみたのですが、動きませんでした。
どうやら、Springと組み合わせる場合はこの手順ではダメのようです。
TomcatのVM引数に追加
-javaagent:eclipselink.jar
applicationContext.xml
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" /> </property> <property name="jpaPropertyMap"> <map> <entry key="eclipselink.weaving" value="true" /> </map> </property> </bean>
Tomcat起動時にエラーが発生
java.lang.IllegalStateException: Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation. at org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver.addTransformer(InstrumentationLoadTimeWeaver.java:88) at org.springframework.context.weaving.AspectJWeavingEnabler.postProcessBeanFactory(AspectJWeavingEnabler.java:69) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:553) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:536) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:362) at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3934) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4429) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardHost.start(StandardHost.java:722) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443) at org.apache.catalina.core.StandardService.start(StandardService.java:516) at org.apache.catalina.core.StandardServer.start(StandardServer.java:710) at org.apache.catalina.startup.Catalina.start(Catalina.java:583) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Springと組み合わせる場合の手順
まず、applicationContext.xmlを次のようにします。
<!-- これを追加 --> <context:load-time-weaver aspectj-weaving="on" class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" /> </property> <property name="jpaPropertyMap"> <map> <entry key="eclipselink.weaving" value="true" /> </map> </property> </bean>
次に、javaagentの値を次のようにspringのものへ変更します。
-javaagent:spring-agent.jar
これで、Lazy Loadingができるようになります。
以下の文字列は、とあるエンティティをtoStringした結果です。
_persistence_productImage_vh={UnitOfWorkQueryValueHolder: not instantiated}
というところがバイトコードエンハンスされたっぽい箇所です。
実行SQLのログを見ても、きちんとLazy Loadingされている模様です。
jp.root42.r42fw_sample_shop.model.entity.Product@1db3f6c[ id=10 productName=弁当9 productNameKana=ベントウキュウ productCode=bento9 price=900 productStatusType=SELLING _persistence_productImage_vh={UnitOfWorkQueryValueHolder: not instantiated} ]
動きそうに見えて動かない設定
ちなみに、以下のように設定しても同等な気がしたので、試しにやってみたのですがダメでした。
applicationContext.xml
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" /> </property> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> ← <context:load-time-weaver>を消してこれを追加 </property> <property name="jpaPropertyMap"> <map> <entry key="eclipselink.weaving" value="true" /> </map> </property> </bean>
java.lang.NullPointerException at org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getMethodReturnType(PrivilegedAccessHelper.java:300) at org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor.getGetMethodReturnType(MethodAttributeAccessor.java:104) at org.eclipse.persistence.mappings.ForeignReferenceMapping.validateBeforeInitialization(ForeignReferenceMapping.java:1373) at org.eclipse.persistence.descriptors.ClassDescriptor.validateBeforeInitialization(ClassDescriptor.java:5123) at org.eclipse.persistence.descriptors.ClassDescriptor.preInitialize(ClassDescriptor.java:3198) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:429) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:406) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.postConnectDatasource(DatabaseSessionImpl.java:666) at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:617) at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:227) at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:255) at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.getServerSession(EntityManagerFactoryImpl.java:111) at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:163) at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:150) at org.springframework.orm.jpa.JpaTransactionManager.createEntityManagerForTransaction(JpaTransactionManager.java:392) at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:320) at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:374) at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:263) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:101) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
エンハンス済みクラスファイルの中身を見てみよう!と試してみたが失敗
次のようなコードでクラスファイルをファイルへダンプして、
jadでデコンパイルしてみようと思いましたが、なぜかShop.classが0バイトに…残念。
final InputStream src = Shop.class.getClassLoader().getResourceAsStream( "/jp/root42/r42fw_sample_shop/model/entity/Shop.class"); final FileOutputStream dst = new FileOutputStream(new File("C:/Temp/Shop.class")); IOUtils.copy(src, dst); IOUtils.closeQuietly(src); IOUtils.closeQuietly(dst);