<?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やらを書くだけだ。