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>

メモ

メモ