という訳で、次はapplicationContext.xml。これは、web.xmlで指定したとおり、WEB-INFの下に置く。
<?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:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">
<context:annotation-config/>
<context:component-scan base-package="com.motchi.tagdiary"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=utf8</value>
</property>
<property name="username">
<value>mytest</value>
</property>
<property name="password">
<value>mytestpass</value>
</property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice" pointcut="@within(org.springframework.stereotype.Service)"/>
</aop:config>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<property name="basePackage" value="com.motchi.tagdiary"/>
<property name="annotationClass" value="com.motchi.tagdiary.common.annotation.SqlMapper"/>
</bean>
</beans>
まず、"@Resource"でいろいろインジェクトできるようにするため、context:annotation-configを使用。
そして、そのインジェクトするBeanも、やはりXMLではなくアノテーションを書くことでSpringに見つけてもらいたいので、context:component-scanも使った。
データベースの設定は、「bean id="dataSource"」のところにユーザ名やパスワードまでベタ書き。今回はDBにMySQLを使うので、ドライバはcom.mysql.jdbc.Driver。普通DBの設定は、外部のXMLファイルやpropertiesファイルに出すところだが、そんなのは後回し。まずはひと通り動くところまで作ってしまおう。
続く「bean id="sqlSessionFactory"」が、mybatisとSpringの架け橋。ここで先ほど定義したデータソースを使う。
そして、ここからが凝りがいのある(かもしれない)ところ。トランザクション境界の設定だ。
今回のアプリのDBアクセスは、こんな風にしたい。
・アクションは、直接DAOを触らず、必ずサービスを介する。
・サービスのメソッドをトランザクション境界とする。
んで、更新系のメソッドは、チェック例外だろうが非チェック例外だろうが、Exceptionの子クラスの例外が発生したらロールバックしたい。
メソッドが更新系かどうかは、メソッド名でわかるようにする。
それ以外のメソッドはSelectするだけ。
・DAOは、サービスから呼ばれて、ただただSQLを実行する。
まず、「bean id="transactionManager"」のところで、データソースを指定し、DataSourceTransactionManagerを作る。これは普通だと思われる。ちなみに、idを「transactionManager」にしておくと、Springが「あーはいはい、例のアレね」と認識してくれて、その下のトランザクションの設定で名前を出す必要がないらしい。
そして次に、aop:config。proxy-target-class="true"にしているのは、サービスはインタフェースではなくクラスにするから。
ああ分かるさ。オブジェクト指向的にはインタフェースの方が望ましいさ。
でもね、多態性を使うわけじゃなし、
HogeHogeServiceってインタフェースにHogeHogeServiceImplってクラスが、
または、
IHogeHogeServiceってインタフェースにHogeHogeServiceってクラスが、
どーせ一対一で対応すんでしょ!
いいじゃんクラス1個で。
で、クラスは「@Service」アノテーションでSpringに認識してもらうので、pointcutは「@within(org.springframework.stereotype.Service)」で決まり。これは、「@Serviceがついたクラスの中のメソッドが対象」という意味らしい。サービスは全部トランザクショナルになってもらいます。
更新系のメソッドは、安直だけど、プレフィックスにinsert, update, deleteを持つものとし、これらの中でExceptionが起こればロールバックをするようにする。これが「tx:advice id="txAdvice"」の中身。
最後の「bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"」で、DAOを自動的に探してもらう。
このMapperScannerConfigurerの使い方、ちょっとぐぐってみたところ、sqlSessionFactoryとbasePackageを指定する例しか出てこなかった。
まあ、普通はそれで事足りるよね。
DAOをどこかのパッケージに集めて、そのパッケージをbasePackageで指定すればいいんだから。
でもね。
パッケージが画面単位や機能単位に分かれていて、それぞれにDAOがあったりすることもあるじゃないですか。
っていう理由を置いといても、サービスを探す方法がアノテーションなんだから、DAOもアノテーションで探してみたい!という好奇心からAPIを調べると、・・・あるじゃないですか、
setAnnotationClass(Class<? extends Annotation> annotationClass)
なんてメソッドが。
そこで、DAOを識別するための俺々アノテーションSqlMapperを作成(中身は空っぽでOK)し、こいつをannotationClassに指定。basePackageはどどーんとルートパッケージにして完成。
SqlMapper.java
package com.motchi.tagdiary.common.annotation;
public @interface SqlMapper {
}
これで準備完了。あとはクラスやらJSPやらを書くだけだ。