Spring+HibernateEntityManager(@Transactionalアノテーション編)
xmlファイルによるトランザクション設定
前回までの設定では、トランザクションの設定は、beans.xml ファイルの以下の箇所で行っています。
<!-- Transaction Advice --> <tx:advice id="readWriteAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- aop --> <aop:config> <!-- for read-write operations --> <aop:pointcut id="readWriteOperationPointCut" expression="execution(* domain.service.UserServiceImpl.registerUser(..))" /> <aop:advisor advice-ref="readWriteAdvice" pointcut-ref="readWriteOperationPointCut" /> </aop:config>
アプリ開発者的には、
「トランザクション境界になるメソッドのところに書いたほうがいいじゃん」
と思えるかもしれません。
ごもっともです。
おいらもそう思います。
たとえば、トランザクションをかけるメソッド名の変更を行うとします。その場合、修正が必要なファイルは以下の2つになります。
保守性悪し o...rz
@Transactional アノテーション
そこで登場するのが @Transactional アノテーションです。
それでは、UserServiceImpl#registerUser(User) のトランザクション設定をアノテーションに変更してみます。
前準備
前準備として、現在のアノテーション設定を削除します。
beans.xml の以下の内容を、全部消しちゃいます。
<!-- Transaction Advice --> <tx:advice id="readWriteAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- aop --> <aop:config> <!-- for read-write operations --> <aop:pointcut id="readWriteOperationPointCut" expression="execution(* domain.service.UserServiceImpl.registerUser(..))" /> <aop:advisor advice-ref="readWriteAdvice" pointcut-ref="readWriteOperationPointCut" /> </aop:config>
かなりさっぱりしましたね。
@Transactionalアノテーション設定
@Transactionalアノテーションの設定は、以下の手順で行います。
beans.xml に、以下のように記述します。トランザクションマネージャの bean が transactionManager という名前でない場合は、transaction-manager アトリビュートにて明示的に指定します。
<!-- for @Transactional annotations --> <tx:annotation-driven transaction-manager="txManager"/>
UserServiceImpl#registerUser(User) メソッドに、@Transactional アノテーションを設定します。
@Transactional public void registerUser(User<Integer> user) { getEntityManager().persist(user); }
これで、以前と同様に、トランザクションが有効になりました。(`・ω・´)
変更後のソース
UserServiceImpl.java
@Transactional アノテーションが追加されました。
package domain.service; import org.springframework.transaction.annotation.Transactional; import infrastracture.ServiceBase; import domain.entity.User; /** * {@link UserService} の実装クラスです。 * * @author beyondseeker * @version $Id$ */ abstract public class UserServiceImpl extends ServiceBase implements UserService<Integer> { /** * {@inheritDoc} */ @Override @Transactional public void registerUser(User<Integer> user) { getEntityManager().persist(user); } /** * {@inheritDoc} * User の実装クラスに対する依存は、アプリケーション層に委譲しています。 */ abstract public User<Integer> newUser(); }
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 "> <!-- for @Transactional annotations --> <tx:annotation-driven transaction-manager="txManager"/> <!-- Actual DataSource --> <bean id="rawDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="" /> </bean> <!-- LazyConnectionDataSourceProxy --> <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"> <property name="targetDataSource" ref="rawDataSource" /> </bean> <!-- Transaction Manager --> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- JpaVendorAdapter --> <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> <!-- EntityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="persistence.xml" /> <property name="jpaVendorAdapter" ref="jpaVendorAdapter" /> <property name="dataSource" ref="dataSource" /> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key="hibernate.hbm2ddl.auto">create-drop</prop> </props> </property> </bean> <!-- EntityManager --> <bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- Entity --> <bean id="user" class="domain.entity.UserImpl" scope="prototype" /> <!-- Service --> <bean id="userService" class="domain.service.UserServiceImpl"> <lookup-method name="newUser" bean="user"/> <property name="entityManager" ref="entityManager"/> </bean> </beans>
メモ
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 アノテーション編)