Spring+HibernateEntityManager(宣言的トランザクション編)
今回は、前回のサンプルを、プログラム的なランザクションから、宣言的なトランザクションに変更してみます。
主な変更点
java.lang.ClassNotFoundException: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
- JpaTransactionManager の使用
- AOPを用いた宣言的トランザクション設定の追加
- 指定された Advice に従い、必要な箇所でトランザクションの設定を行い、情報を TransactionSynchronizationManager に設定します。
- SharedEntityManagerBean の使用
- EntityManagerFactory から直接 EntityManager を取得すると、スレッドに紐付けたトランザクション情報を参照できないため、SharedEntityManagerBean を使用して、TransactionSynchronizationManager から EntityManager を使用するようにします。SharedEntityManager とでも言うべきでしょうか。
ソース
sample.A
サンプルのエンティティです。このファイルは特に変更点はありません。
package sample; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class A { @Id @GeneratedValue private int id; }
sample.Service
宣言的トランザクション用に設定された EntityManager を使用するように変更。
package sample; import javax.persistence.EntityManager; import org.springframework.orm.jpa.JpaDialect; import org.springframework.orm.jpa.support.JpaDaoSupport; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; public class Service { /** * DI される EntityManager */ private EntityManager em; // Shared EntityManager proxy for target factory [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@1b4fe4d] /** * @param em */ public void setEm(EntityManager em) { this.em = em; } public void persistA() { // em は Shared EntityManager proxy なので、 // TransactionSynchronizationManager から EntityManagerの実体を取得し、 // それを用いて永続化処理を行います。 em.persist(new A()); } }
sample.SampleMain
このソース内に書かれた処理そのものは変わりませんが、AOPにより行われる処理についてコメント。
package sample; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SampleMain { // A を新規作成し、DB に保存 public static void main(String[] args) throws Exception { // ApplicationContext の取得 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // Service の取得 // beans-xmlの以下の adviser 設定により、宣言的トランザクション用の AOP がかかったクラスが返されます。 // <aop:advisor advice-ref="allOperationAdvice" // pointcut-ref="allOperation" /> // 例:Service$$EnhancerByCGLIB$$c3b5bf5b Service service = (Service) ctx.getBean("service"); // A を新規作成し、DB に保存。 // // このメソッドの開始前に、AOPでトランザクションの開始および終了処理が入ります。 // // ■ 開始処理(細部は省略) // org.springframework.transaction.interceptor.TransactionInterceptor 経由で // org.springframework.orm.jpa.JpaTransactionManager の doBegin メソッドが呼ばれ、 // EntityManagerHolder が存在しなければ、createEntityManagerForTransaction // メソッドにてトランザクション用の EntityManager を作成し、 // それを用いて EntityManagerHolder を新規作成し、 // org.springframework.transaction.support.TransactionSynchronizationManager#bindResource // にて、スレッドとEntityManagerFactoryの組み合わせにユニークなEntityManagerがバインドされます。 // ・バインドのキーは、org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean(の proxy) // ・バインドの値は、org.springframework.orm.jpa.EntityManagerHolder // その後、トランザクションが開始。 service.persistA(); // ■ 終了処理(細部は省略) // スレッドにバインドしたトランザクション情報を削除します。 } }
beans.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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" > <!-- Transaction Manager の設定 --> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="emf" /> </bean> <!-- ベンダ依存の情報を取得するためのアダプタ --> <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> <!-- persistence.xml の位置指定や LTW 等の設定も可能な FactoryBean --> <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="persistence.xml" /> <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> </bean> <!-- TransactionSynchronizationManager にバインドされたエンティティマネージャを取得可能な bean。 普通に EntityManagerFactory#getEntityManager() すると、宣言的トランザクションによって スレッドとemfの組み合わせに関連付けられた EntityManager を取得できません。 --> <bean id="em" class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> <property name="entityManagerFactory" ref="emf" /> </bean> <!-- トランザクションのアドバイスです。 --> <tx:advice id="allOperationAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- Service --> <bean id="service" class="sample.Service"> <property name="em" ref="em" /> </bean> <!-- aop --> <aop:config> <aop:pointcut id="allOperation" expression="execution(* sample.Service.persistA())" /> <!-- advisor を設定すると、指定された pointcut に対して、指定された advice が設定される。 --> <aop:advisor advice-ref="allOperationAdvice" pointcut-ref="allOperation" /> </aop:config> </beans>
persistence.xml
このファイルは特に変更ありません。
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0" > <persistence-unit name="manager" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <!-- データソースの設定 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <property name="hibernate.connection.username" value="root" /> <property name="hibernate.connection.password" value="" /> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test" /> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> <!-- テスト用プログラムなので、毎回DBのテーブルをを新規作成するように設定。 --> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> </properties> </persistence-unit> </persistence>
参考
links
第1回 Spring+HibernateEntityManager(HibernateEntityManager単体編)
第2回 Spring+HibernateEntityManager(とりあえずSpring編)
第3回 Spring+HibernateEntityManager(宣言的トランザクション編)
第4回 Spring+HibernateEntityManager(Spring+DDDっぽく編)
第5回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その2)
第6回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その3)
第7回 Spring+HibernateEntityManager(Spring+DDDっぽく編 その4)
第8回 Spring+HibernateEntityManager(@Transactionalアノテーション編)
第9回 Spring+HibernateEntityManager(@Required編)
第10回 Spring+HibernateEntityManager(XMLからの外部リソース参照編)
第11回 Spring+HibernateEntityManager(AspectJ AOP with Load-Time-Weaver編)
第12回 Spring+HibernateEntityManager(DBCPのvalidationQuery編)
第13回 Spring+HibernateEntityManager(@Resource編)
第14回 Spring+HibernateEntityManager(コンポーネント自動検出 with アノテーション編)
第15回 Spring+HibernateEntityManager(コンポーネント自動検出 without アノテーション編)