狠狠撸

狠狠撸Share a Scribd company logo
Spring で DAO

船戸隆(エーティーエルシステム
ズ)
鈴木雄介(アークランプ)
目的



? Spring を使って DAO を使う方法を理解する
? DAO を使う場合の注意点を理解する
? 皆がどんな風に DAO を使っているのか共有する
アジェンダ
? DAO とは
? DAO の設計
? Spring で DAO
  – 実装
  – 設定
  – テスト
? GenericDao with Spring
? 気になること
DAO とは
DAO とは
? Data Access
  Object の略
  – J2EE パターンで
    紹介
  – 永続化層におい
    て様々なデータ
    ソースを抽象化
    する
  – 今回は
    RDB ( JDBC)
    前提
                  http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
DAO とは
? メリット
 – SQL が集約されるためメンテナンス性が高い
 – TransferObject になるので Java コードから扱
   いやすい。型重要
? デメリット
 – 他層からデータアクセスの柔軟性が失われる
 – TransferObject を作成するコストが高い
DAO の設計
DAO の設計
? DAO のインターフェース設計
 – CRUD + Finder メソッド
 – パラメタオブジェクトを利用する
 – Tiger 便利
  ? TypeSafeEnum
  ? Generics
DAO の設計
? CRUD + Finder メソッド
 – CRUD ( Create 、 Read 、 Update 、 Delete
   )は、 TransferObject をまるごとやりとり
   ? Read は ID による検索
 – ID 以外の検索は Finder メソッドとして、ある
   程度の目的別に用意
DAO の設計
? パラメタオブジェクトを利用する
   – DAO の Finder メソッドにおいて、パラメタ
     が増えるたびにインターフェースが変わるの
     はいや
   – そこでパラメタオブジェクトとして抽象化
      ? パタメタが増えても、 DAO のインターフェースに
public  影響を与えない
      interface BasicDao {
  List findByCriteria(BasicFindCriteria criteria);
}
                           public class BasicFindCriteria {
                             private String someKey;
                           }
DAO の設計
? Tiger 便利( JavaSE5 )
 – 型重要
 – TypeSafeEnum
   public enum AnyEnum {
     type1, type2
   }

 – Generics
   public interface BasicDao {
     List<BasicTransferObject>
        findByCriteria(BasicFindCriteria criteria);
   }
Spring で DAO
Spring で DAO
? 実装のコツ
 – 基本は ORM を使う
 – Spring の ORM サポートクラスを使う
? 設定のコツ
 – AOP でトランザクション設定
? テストのコツ
 – Spring で用意されたテストサポートクラスを
   使う
 – DAO を使うクラスをテストする場合はモック
   を使う
Spring で DAO – 実装
Spring で DAO – 実装
? 基本は ORM を使う
 – とりあえず便利
 – でも無理に使うことはない
? Spring の ORM サポートクラスを使う
 – Exception の違いを吸収
   ? Hibernate→HibernateException
   ? iBATIS→SQLException
 – try catch を書かなくて済むのでコードがすっきり
 – 例外で処理を判定するコードは書かない
   ? org.hibernate.NonUniqueObjectException で判定するな
     ど
Spring で DAO – 実装

? 実装の流れ
  – TransferObject を作る
  – DAO のインターフェースを作る
  – DAO の実装をする
  – マッピングファイル書く
  – Spring に Bean 定義する
? ORM で実装サンプル
  – 今回は Hibernate 、 iBATIS そして???
Spring で DAO – 実装
? 実際に DAO を実装してみる



               CREATE TABLE ELECTRIC_GUITAR (
                 ID varchar(40) NOT NULL,
                 NAME varchar(200) ,
                 MANUFACTURE varchar(20),
                 CRAFTED_DATE datetime,
                 PRICE int(11),
                 PRIMARY KEY (ID)
               )
Spring で DAO – 実装
? TransferObject を作る
   public class ElectricGuitar {

     private String id;
                                            Java5 の
                                             Enum
     private String name;

     private ManufactureEnum manufacture;

     private Date craftedDate;

     private Integer price;
           ?
           ?
           ?
Spring で DAO – 実装
? DAO のインターフェースを作る
  public interface ElectricGuitarDao {

      List<ElectricGuitar> findAll();

      ElectricGuitar load(String id);

      String insert(ElectricGuitar electricGuitar);

      void update(ElectricGuitar electricGuitar);

      void delete(ElectricGuitar electricGuitar);

  }
Spring で DAO – 実装
? Hibernate のサンプル
  public class ElectricGuitarHibernateDaoImpl extends
           HibernateDaoSupport implements ElectricGuitarDao {

    public List<ElectricGuitar> findAll() {
      return getHibernateTemplate().loadAll(ElectricGuitar.class);
    }
          ?
          ?
          ?
Spring で DAO – 実装
? マッピングファイルを書く
   – Hibernate のマッピングファイル
 src/main/resources/ElectricGuitar.hbm.xml
<hibernate-mapping package="jp.springframework.vol2.domain">
 <class name="ElectricGuitar" table="ELECTRIC_GUITAR">
  <id name="id" column="ID" type="string" length="40">
   <generator class="uuid" />
  </id>
  <property name="name" column="NAME" type="string" length="200"/>
  <property name="manufacture" column="MANUFACTURE"
   type="jp.springframework.vol2.domain.enums.hibernate.ManufactureType“ length="20"/>
  <property name="craftedDate" column="CRAFTED_DATE" type="timestamp" />
  <property name="price" column="PRICE" type="integer" />
 </class>
Spring で DAO – 実装
 ? Spring の設定をする
      –Spring の ApplicationContext の設定
    src/main/resources/context/applicationContextHibernate.xml
    <bean id="electricGuitarDao"
   class="jp.springframework.vol2.dao.hibernate.ElectricGuitarHibernateDaoImpl">
      <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean id="sessionFactory"
   class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      <property name="dataSource" ref="dataSource"/>
      <property name="mappingLocations">
       <list>
        <value>classpath:hibernate/ElectricGuitar.hbm.xml</value>
       </list>
      </property>

org.hibernate.SessionFactory を組み立てくれる
Spring で DAO – 実装
? iBATIS のサンプル
public class ElectricGuitarIbatisDaoImpl extends SqlMapClientDaoSupport implements
     ElectricGuitarDao {

  public List<ElectricGuitar> findAll() {
    return getSqlMapClientTemplate().queryForList("ElectricGuitar.findAll");
  }

 public ElectricGuitar load(String id) {
   return (ElectricGuitar) getSqlMapClientTemplate().queryForObject("ElectricGuitar.load", id);
 }
   ?
   ?
   ?
Spring で DAO – 実装
? マッピングファイルを書く
 – iBATIS のマッピングファイル
  src/main/resources/ibatis/ElectricGuitar.sqlmap.xml
   <sqlMap namespace="ElectricGuitar">

   <typeAlias alias="guitarParam" type="jp.springframework.vol2.domain.ElectricGuitar"/>

    <resultMap id="guitar" class="jp.springframework.vol2.domain.ElectricGuitar">
     <result property="id" column="ID" />
     <result property="name" column="NAME" />
     <result property="manufacture" column="MANUFACTURE" typeHandler="ManufactureEnum"/>
     <result property="craftedDate" column="CRAFTED_DATE" />
     <result property="price" column="PRICE" />
    </resultMap>

    <select id="findAll" resultMap="guitar">
   SELECT ID, NAME, MANUFACTURE, CRAFTED_DATE, PRICE FROM ELECTRIC_GUITAR
    </select>
      ?
      ?
      ?
Spring で DAO – 実装
   ? iBATIS の Spring 設定
    src/main/resources/context/applicationContextIbatis.xml

<bean id="sqlMapClient"
 class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
 <property name="configLocation" value="classpath:ibatis/sqlmap-config.xml" />
 <property name="dataSource" ref="dataSource" />
</bean>
                                                   com.ibatis.sqlmap.client. SqlMapClient を組み立てくれ
                                                   る
<bean id="electricGuitarDao" class="jp.springframework.vol2.dao.ibatis.ElectricGuitarIbatisDaoImpl">
 <property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
Spring で DAO – 実装
? Spring-S2DAO のサンプル
  – S2DAO はインターフェースのみで動作する
    ため実装クラスはありません




Spring-S2Dao
Via:http://d.hatena.ne.jp/n-ichimura/
Spring で DAO – 実装
? S2DAO の設定
 – 基本的に設定レス
 – Java ソースコードにメタデータを記述するだ
   け
 – メソッドを命名規則に合わせることで SQL 文
   の記述が不要
Spring で DAO – 実装
? Spring-S2DAO の Spring の設定
   <bean class="framework.autoregister.FileSystemBeanAutoRegister">
      <property name="addPackageName">
         <value>jp.springframework.vol2.dao</value>
      </property>
      <property name="addClassNames">
         <value>.*Dao</value>
      </property>
      <property name="ignorePackageName">
         <value>jp.springframework.vol2.dao</value>
      </property>
      <property name="ignoreClassNames">
         <value>ElectricGuitarGenericDao,IteratorDao</value>
      </property>
    </bean>

   S2Container の AutoRegister に似た機能
   ( org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister )
   なので個別に DAO の Bean 定義をしません
Spring で DAO – 実装
? ORM サポートクラスは他にもいろいろ
 – org.springframework.orm パッケージ以下
 – Hibernate 、 iBATIS 、 TopLink 、 JPA 、 etc…



 詳しくは
Spring で DAO – 設定
Spring で DAO – 設定
    ? AOP でトランザクション設定
        – トランザクション単位を考えよう
        src/main/resources/context/applicationContext-transaction.xml
              <aop:config>
                <aop:advisor pointcut="execution(* *..*MyBusinessLogic.*(..))"
                  advice-ref="txAdvice"/>
               </aop:config>
  Spring 2.X から使え
  る             <tx:advice id="txAdvice">
                <tx:attributes>
                  <tx:method name="calculateAmount" read-only="true"/>
                </tx:attributes>
               </tx:advice>
 ビジネスロジックにトランザクションをかけた場合
その中の DAO のトランザクションはすべて同一になる
Spring で DAO – テス
         ト
Spring で DAO – テスト
? Spring で用意されたテストサポートクラスを使う
  ( org.springframework.test.AbstractTransactionalDataS
  ourceSpringContextTests )
   – Spring のコンテナを自動で初期化してくれる
      @Override
       protected String[] getConfigLocations() {
         return new String[] {"classpath:context/applicationContextHibernate.xml" };
       }


   – Setter があれば自動で Inject してくれる
     private ElectricGuitarDao electricGuitarDao;
     public void setElectricGuitarDao(ElectricGuitarDao newElectricGuitarDao) {
          this.electricGuitarDao = newElectricGuitarDao;
       }



   – 自動でトランザクションの開始&ロールバック
   – テスト用 SQL 実行メソッドがある
     executeSqlScript("classpath:testdata.sql", false);
Spring で DAO – テスト
Hibernate 用 DAO のサンプルコード
    public class ElectricGuitarHibernateDaoImplTest extends
        AbstractTransactionalDataSourceSpringContextTests {

      private ElectricGuitarDao electricGuitarDao;

    public void setElectricGuitarDao(ElectricGuitarDao newElectricGuitarDao) {
        this.electricGuitarDao = newElectricGuitarDao;
      }

      @Override
      protected String[] getConfigLocations() {
        return new String[] {"classpath:context/applicationContextHibernate.xml" };
      }

      public void testInsert() {
        ElectricGuitar guitar = new ElectricGuitar();
        guitar.setManufacture(ManufactureEnum.fender);
        guitar.setName("'56 Stratocaster");
        guitar.setCraftedDate(new Date());
        guitar.setPrice(264320);
        String id = this.electricGuitarDao.insert(guitar);

       assertEquals(id, this.electricGuitarDao.load(id).getId());
     }    
       ?
       ?
Spring で DAO- テスト
? DAO を使うクラスをテストする場
  合はモックを使う
 – 圧倒的なパフォーマンスの差
   ? インテグレーションテストを行う際に
     威力を発揮
   ? MyBusinessLogicDaoTest
   ? MyBusinessLogicMockTest


 – 作業の非同期化
   ? DAO の実装なしでもビジネスロジック
     のテストを書ける
Spring で DAO – テスト
?   モックを使った実装サンプル
    –   ビジネスロジック( MyBusinessLogic )では、割
        引入り合計金額計算( calcuateAmount )を行う
Spring で DAO – テスト
? easymock ( http://www.easymock.org/)
? 手順
  1. モックを準備
  2.モックの動きを用意
  3.テストを実施
  4.モックの動きを確認
Spring で DAO – テスト
? 手順 1: モックを準備
   private MyBusinessLogicImpl myBusinessLogicImpl;
   private ElectricGuitarDao mock;

   protected void setUp() {
     mock = createMock(ElectricGuitarDao.class);
     myBusinessLogicImpl = new MyBusinessLogicImpl();
     myBusinessLogicImpl.setElectricGuitarDao(mock);
   }
インターフェースを指定し
てモックを作る
( static import も便利だ
よ)
                   あとは普通に Inject するだ
                   け
Spring で DAO – テスト
? 手順 2: モックの動きを用意
         これを呼ばれる               これが返るよ
         と、
 ElectricGuitar a = new ElectricGuitar();
 a.setManufacture(ManufactureEnum.gretsch);
 a.setPrice(100000);
 expect(mock.load("1")).andReturn(a);
 expectLastCall().times(2);
                                         それを 2 回
 ElectricGuitar b = new ElectricGuitar();
 b.setManufacture(ManufactureEnum.fender);
 b.setPrice(200000);
 expect(mock.load("2")).andReturn(b);
                                 これで用意完
 replay(mock);                   了
Spring で DAO – テスト
   ? 手順 3: テストを実施
       – 普通にテストするだけ

long amount = myBusinessLogicImpl.calculateAmount(new String[] {"1" });
assertEquals(80000, amount);

amount = myBusinessLogicImpl.calculateAmount(new String[] {"1", "2" });
assertEquals(260000, amount);
Spring で DAO – テスト
? 手順 4: モックの動きを確認
 – 用意した通りに動いたことを確認


        verify(mock);
GenericDao with Spring
GenericDao with Spring
? GenericDao とは
 – Generics と AOP を使った DAO 支援フレーム
   ワーク
   ? http://www-06.ibm.com/jp/developerworks/java/060705/j_j-genericdao.shtml

 – 実装クラスレスで DAO が作れちゃう。 O/R
   マッパのラッパとして非常に便利
 – Spring と一緒に使うべし
   ? 今回は Hibernate を利用
GenericDao with Spring

? 実装サンプル
 – Generics を使うことで、型つきの CRUD メ
   ソッドが準備される
 – Finder メソッドは、メソッド名と同じ query
   を用意する
GenericDao with Spring
? インターフェースとマッピングファイル
  だけ interface ElectricGuitarGenericDao
   public
      extends GenericDao<ElectricGuitar, String>{
     public ElectricGuitar findByName(String name);
     public List<ElectricGuitar> findAll();
   }
  <hibernate-mapping package="jp.springframework.vol2.domain">
    <class name="ElectricGuitar" table="ELECTRIC_GUITAR">
      <id name="id" column="ID" type="string" length="40">
      …
    </class>
    <query name="ElectricGuitar.findByName">
      <![CDATA[select e from ElectricGuitar e where e.name = ? ]]>
    </query>
    <query name="ElectricGuitar.findAll">
      <![CDATA[select e from ElectricGuitar e order by e.price]]>
    </query>
  </hibernate-mapping>
GenericDao with Spring
? 仕組み
 – CRUD の実装は普通
 – Finder の実装は AOP


? コード見てね
気になること
気になること
? そもそも O/R マッパって遅い
 – マッピングコストが高い
  ? 不必要な項目も取得してしまう
? 現状では解決は難しい
気になること
? 返値 List の Finder で OutOfMemory にな
  る
  – バッチ処理やファイルダウンロード処理であ
    りがち
  – レコードの数だけマッピングしてしまう
? そういう場合は Iterator を使おう
  – next() するごとにマッピング
気になること
? 複数レコードの更新?削除処理が遅い
 – CRUD しかないと、レコードの数だけ
   Update や Delete を繰り返し
? 専用メソッドを用意しよう
 – Update 文や Delete 文
 – StoredProcedure
気になること
? キャッシュしたい
 – DAOでキャッシュ
  ? Hibernate は自動でやってくれる
  ? OSCache などで自分でキャッシュ
気になること
? パフォーマンス計測したい
       – AOP をつかうと楽
             ? Bean 定義したクラスのメソッドの実行時間計測
public class TraceInterceptor implements MethodInterceptor {
  private static Log log = LogFactory.getLog(TraceInterceptor.class);
  public static ThreadLocal<String> local = new ThreadLocal<String>();
  public Object invoke(MethodInvocation invocation) throws Throwable {

      String key = local.get();
      if ( StringUtils.isEmpty(key) || "null".equals(key) ) {
          key = UUID.randomUUID().toString();
          local.set(key);
      }
      log.debug("," + key + "," + System.currentTimeMillis() + "," + getInvocationDescription(invocation) + ",start");
      Object value = invocation.proceed();
      log.debug("," + key + "," + System.currentTimeMillis() + "," + getInvocationDescription(invocation) + ",end");
      return value;
  }
気になること

? N+ 1問題
 – 親レコードの数だけ子レコードを取得
   する SQL が発行されてしまう
   ? 注文と注文商品みたいな関係
? Lazy ロードで回避できるが???
 – トランザクション注意!
   ? トランザクション終了前に子レコードを取
     得する
気になること

? SQL インジェクション攻撃
? プリペアドステートメント推奨
 – SQL の構造が変わらない
 – とはいっても素のステートメントも使
   いたい???
  ? MySQL のクエリーキャッシュが効かない
  ? 使う場合きちんとエスケープする
気になること
? エンティティ露出問題
 – ビュー層で必要とされるモデルとのミスマッチ
    ? ビュー層でマスターデータ参照したい
    – マスターにある項目を表示したいのだけど
      TransferObject には ID しかはいっていない???
 – 詰め替え問題
  ? DTO に誰がどこでつめなおすの?
  ? ビジネスロジック層で泣きながら DTO につめる
? 設計上の問題なので解決策があるわけではない
Q&A
ライセンスについて
?   JSUG マスコットアイコン(本スライド左下)が残されている場合に限り、本作品
    (またそれを元にした派生作品)の複製?頒布?表示?上演を認めます。

?   非商用目的に限り、本作品(またそれを元にした派生作品)の複製?頒布?表示?
    上演を認めます。

?   本作品のライセンスを遵守する限り、派生作品を頒布することを許可します。

More Related Content

Springて?dao 20070413

  • 2. 目的 ? Spring を使って DAO を使う方法を理解する ? DAO を使う場合の注意点を理解する ? 皆がどんな風に DAO を使っているのか共有する
  • 3. アジェンダ ? DAO とは ? DAO の設計 ? Spring で DAO – 実装 – 設定 – テスト ? GenericDao with Spring ? 気になること
  • 5. DAO とは ? Data Access Object の略 – J2EE パターンで 紹介 – 永続化層におい て様々なデータ ソースを抽象化 する – 今回は RDB ( JDBC) 前提 http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
  • 6. DAO とは ? メリット – SQL が集約されるためメンテナンス性が高い – TransferObject になるので Java コードから扱 いやすい。型重要 ? デメリット – 他層からデータアクセスの柔軟性が失われる – TransferObject を作成するコストが高い
  • 8. DAO の設計 ? DAO のインターフェース設計 – CRUD + Finder メソッド – パラメタオブジェクトを利用する – Tiger 便利 ? TypeSafeEnum ? Generics
  • 9. DAO の設計 ? CRUD + Finder メソッド – CRUD ( Create 、 Read 、 Update 、 Delete )は、 TransferObject をまるごとやりとり ? Read は ID による検索 – ID 以外の検索は Finder メソッドとして、ある 程度の目的別に用意
  • 10. DAO の設計 ? パラメタオブジェクトを利用する – DAO の Finder メソッドにおいて、パラメタ が増えるたびにインターフェースが変わるの はいや – そこでパラメタオブジェクトとして抽象化 ? パタメタが増えても、 DAO のインターフェースに public 影響を与えない interface BasicDao { List findByCriteria(BasicFindCriteria criteria); } public class BasicFindCriteria { private String someKey; }
  • 11. DAO の設計 ? Tiger 便利( JavaSE5 ) – 型重要 – TypeSafeEnum public enum AnyEnum { type1, type2 } – Generics public interface BasicDao { List<BasicTransferObject> findByCriteria(BasicFindCriteria criteria); }
  • 13. Spring で DAO ? 実装のコツ – 基本は ORM を使う – Spring の ORM サポートクラスを使う ? 設定のコツ – AOP でトランザクション設定 ? テストのコツ – Spring で用意されたテストサポートクラスを 使う – DAO を使うクラスをテストする場合はモック を使う
  • 14. Spring で DAO – 実装
  • 15. Spring で DAO – 実装 ? 基本は ORM を使う – とりあえず便利 – でも無理に使うことはない ? Spring の ORM サポートクラスを使う – Exception の違いを吸収 ? Hibernate→HibernateException ? iBATIS→SQLException – try catch を書かなくて済むのでコードがすっきり – 例外で処理を判定するコードは書かない ? org.hibernate.NonUniqueObjectException で判定するな ど
  • 16. Spring で DAO – 実装 ? 実装の流れ – TransferObject を作る – DAO のインターフェースを作る – DAO の実装をする – マッピングファイル書く – Spring に Bean 定義する ? ORM で実装サンプル – 今回は Hibernate 、 iBATIS そして???
  • 17. Spring で DAO – 実装 ? 実際に DAO を実装してみる CREATE TABLE ELECTRIC_GUITAR ( ID varchar(40) NOT NULL, NAME varchar(200) , MANUFACTURE varchar(20), CRAFTED_DATE datetime, PRICE int(11), PRIMARY KEY (ID) )
  • 18. Spring で DAO – 実装 ? TransferObject を作る public class ElectricGuitar { private String id; Java5 の Enum private String name; private ManufactureEnum manufacture; private Date craftedDate; private Integer price; ? ? ?
  • 19. Spring で DAO – 実装 ? DAO のインターフェースを作る public interface ElectricGuitarDao { List<ElectricGuitar> findAll(); ElectricGuitar load(String id); String insert(ElectricGuitar electricGuitar); void update(ElectricGuitar electricGuitar); void delete(ElectricGuitar electricGuitar); }
  • 20. Spring で DAO – 実装 ? Hibernate のサンプル public class ElectricGuitarHibernateDaoImpl extends HibernateDaoSupport implements ElectricGuitarDao { public List<ElectricGuitar> findAll() { return getHibernateTemplate().loadAll(ElectricGuitar.class); } ? ? ?
  • 21. Spring で DAO – 実装 ? マッピングファイルを書く – Hibernate のマッピングファイル src/main/resources/ElectricGuitar.hbm.xml <hibernate-mapping package="jp.springframework.vol2.domain"> <class name="ElectricGuitar" table="ELECTRIC_GUITAR"> <id name="id" column="ID" type="string" length="40"> <generator class="uuid" /> </id> <property name="name" column="NAME" type="string" length="200"/> <property name="manufacture" column="MANUFACTURE" type="jp.springframework.vol2.domain.enums.hibernate.ManufactureType“ length="20"/> <property name="craftedDate" column="CRAFTED_DATE" type="timestamp" /> <property name="price" column="PRICE" type="integer" /> </class>
  • 22. Spring で DAO – 実装 ? Spring の設定をする –Spring の ApplicationContext の設定 src/main/resources/context/applicationContextHibernate.xml <bean id="electricGuitarDao" class="jp.springframework.vol2.dao.hibernate.ElectricGuitarHibernateDaoImpl"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingLocations"> <list> <value>classpath:hibernate/ElectricGuitar.hbm.xml</value> </list> </property> org.hibernate.SessionFactory を組み立てくれる
  • 23. Spring で DAO – 実装 ? iBATIS のサンプル public class ElectricGuitarIbatisDaoImpl extends SqlMapClientDaoSupport implements ElectricGuitarDao { public List<ElectricGuitar> findAll() { return getSqlMapClientTemplate().queryForList("ElectricGuitar.findAll"); } public ElectricGuitar load(String id) { return (ElectricGuitar) getSqlMapClientTemplate().queryForObject("ElectricGuitar.load", id); }    ?    ?    ?
  • 24. Spring で DAO – 実装 ? マッピングファイルを書く – iBATIS のマッピングファイル src/main/resources/ibatis/ElectricGuitar.sqlmap.xml <sqlMap namespace="ElectricGuitar"> <typeAlias alias="guitarParam" type="jp.springframework.vol2.domain.ElectricGuitar"/> <resultMap id="guitar" class="jp.springframework.vol2.domain.ElectricGuitar"> <result property="id" column="ID" /> <result property="name" column="NAME" /> <result property="manufacture" column="MANUFACTURE" typeHandler="ManufactureEnum"/> <result property="craftedDate" column="CRAFTED_DATE" /> <result property="price" column="PRICE" /> </resultMap> <select id="findAll" resultMap="guitar"> SELECT ID, NAME, MANUFACTURE, CRAFTED_DATE, PRICE FROM ELECTRIC_GUITAR </select>    ?    ?    ?
  • 25. Spring で DAO – 実装 ? iBATIS の Spring 設定 src/main/resources/context/applicationContextIbatis.xml <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="classpath:ibatis/sqlmap-config.xml" /> <property name="dataSource" ref="dataSource" /> </bean> com.ibatis.sqlmap.client. SqlMapClient を組み立てくれ る <bean id="electricGuitarDao" class="jp.springframework.vol2.dao.ibatis.ElectricGuitarIbatisDaoImpl"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean>
  • 26. Spring で DAO – 実装 ? Spring-S2DAO のサンプル – S2DAO はインターフェースのみで動作する ため実装クラスはありません Spring-S2Dao Via:http://d.hatena.ne.jp/n-ichimura/
  • 27. Spring で DAO – 実装 ? S2DAO の設定 – 基本的に設定レス – Java ソースコードにメタデータを記述するだ け – メソッドを命名規則に合わせることで SQL 文 の記述が不要
  • 28. Spring で DAO – 実装 ? Spring-S2DAO の Spring の設定 <bean class="framework.autoregister.FileSystemBeanAutoRegister"> <property name="addPackageName"> <value>jp.springframework.vol2.dao</value> </property> <property name="addClassNames"> <value>.*Dao</value> </property> <property name="ignorePackageName"> <value>jp.springframework.vol2.dao</value> </property> <property name="ignoreClassNames"> <value>ElectricGuitarGenericDao,IteratorDao</value> </property> </bean> S2Container の AutoRegister に似た機能 ( org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister ) なので個別に DAO の Bean 定義をしません
  • 29. Spring で DAO – 実装 ? ORM サポートクラスは他にもいろいろ – org.springframework.orm パッケージ以下 – Hibernate 、 iBATIS 、 TopLink 、 JPA 、 etc… 詳しくは
  • 30. Spring で DAO – 設定
  • 31. Spring で DAO – 設定 ? AOP でトランザクション設定 – トランザクション単位を考えよう src/main/resources/context/applicationContext-transaction.xml <aop:config> <aop:advisor pointcut="execution(* *..*MyBusinessLogic.*(..))"     advice-ref="txAdvice"/> </aop:config> Spring 2.X から使え る <tx:advice id="txAdvice"> <tx:attributes> <tx:method name="calculateAmount" read-only="true"/> </tx:attributes> </tx:advice> ビジネスロジックにトランザクションをかけた場合 その中の DAO のトランザクションはすべて同一になる
  • 32. Spring で DAO – テス ト
  • 33. Spring で DAO – テスト ? Spring で用意されたテストサポートクラスを使う ( org.springframework.test.AbstractTransactionalDataS ourceSpringContextTests ) – Spring のコンテナを自動で初期化してくれる @Override protected String[] getConfigLocations() { return new String[] {"classpath:context/applicationContextHibernate.xml" }; } – Setter があれば自動で Inject してくれる private ElectricGuitarDao electricGuitarDao; public void setElectricGuitarDao(ElectricGuitarDao newElectricGuitarDao) { this.electricGuitarDao = newElectricGuitarDao; } – 自動でトランザクションの開始&ロールバック – テスト用 SQL 実行メソッドがある executeSqlScript("classpath:testdata.sql", false);
  • 34. Spring で DAO – テスト Hibernate 用 DAO のサンプルコード public class ElectricGuitarHibernateDaoImplTest extends AbstractTransactionalDataSourceSpringContextTests { private ElectricGuitarDao electricGuitarDao; public void setElectricGuitarDao(ElectricGuitarDao newElectricGuitarDao) { this.electricGuitarDao = newElectricGuitarDao; } @Override protected String[] getConfigLocations() { return new String[] {"classpath:context/applicationContextHibernate.xml" }; } public void testInsert() { ElectricGuitar guitar = new ElectricGuitar(); guitar.setManufacture(ManufactureEnum.fender); guitar.setName("'56 Stratocaster"); guitar.setCraftedDate(new Date()); guitar.setPrice(264320); String id = this.electricGuitarDao.insert(guitar); assertEquals(id, this.electricGuitarDao.load(id).getId()); }        ?    ?
  • 35. Spring で DAO- テスト ? DAO を使うクラスをテストする場 合はモックを使う – 圧倒的なパフォーマンスの差 ? インテグレーションテストを行う際に 威力を発揮 ? MyBusinessLogicDaoTest ? MyBusinessLogicMockTest – 作業の非同期化 ? DAO の実装なしでもビジネスロジック のテストを書ける
  • 36. Spring で DAO – テスト ? モックを使った実装サンプル – ビジネスロジック( MyBusinessLogic )では、割 引入り合計金額計算( calcuateAmount )を行う
  • 37. Spring で DAO – テスト ? easymock ( http://www.easymock.org/) ? 手順 1. モックを準備 2.モックの動きを用意 3.テストを実施 4.モックの動きを確認
  • 38. Spring で DAO – テスト ? 手順 1: モックを準備 private MyBusinessLogicImpl myBusinessLogicImpl; private ElectricGuitarDao mock; protected void setUp() { mock = createMock(ElectricGuitarDao.class); myBusinessLogicImpl = new MyBusinessLogicImpl(); myBusinessLogicImpl.setElectricGuitarDao(mock); } インターフェースを指定し てモックを作る ( static import も便利だ よ) あとは普通に Inject するだ け
  • 39. Spring で DAO – テスト ? 手順 2: モックの動きを用意 これを呼ばれる これが返るよ と、 ElectricGuitar a = new ElectricGuitar(); a.setManufacture(ManufactureEnum.gretsch); a.setPrice(100000); expect(mock.load("1")).andReturn(a); expectLastCall().times(2); それを 2 回 ElectricGuitar b = new ElectricGuitar(); b.setManufacture(ManufactureEnum.fender); b.setPrice(200000); expect(mock.load("2")).andReturn(b); これで用意完 replay(mock); 了
  • 40. Spring で DAO – テスト ? 手順 3: テストを実施 – 普通にテストするだけ long amount = myBusinessLogicImpl.calculateAmount(new String[] {"1" }); assertEquals(80000, amount); amount = myBusinessLogicImpl.calculateAmount(new String[] {"1", "2" }); assertEquals(260000, amount);
  • 41. Spring で DAO – テスト ? 手順 4: モックの動きを確認 – 用意した通りに動いたことを確認 verify(mock);
  • 43. GenericDao with Spring ? GenericDao とは – Generics と AOP を使った DAO 支援フレーム ワーク ? http://www-06.ibm.com/jp/developerworks/java/060705/j_j-genericdao.shtml – 実装クラスレスで DAO が作れちゃう。 O/R マッパのラッパとして非常に便利 – Spring と一緒に使うべし ? 今回は Hibernate を利用
  • 44. GenericDao with Spring ? 実装サンプル – Generics を使うことで、型つきの CRUD メ ソッドが準備される – Finder メソッドは、メソッド名と同じ query を用意する
  • 45. GenericDao with Spring ? インターフェースとマッピングファイル だけ interface ElectricGuitarGenericDao public extends GenericDao<ElectricGuitar, String>{ public ElectricGuitar findByName(String name); public List<ElectricGuitar> findAll(); } <hibernate-mapping package="jp.springframework.vol2.domain"> <class name="ElectricGuitar" table="ELECTRIC_GUITAR"> <id name="id" column="ID" type="string" length="40"> … </class> <query name="ElectricGuitar.findByName"> <![CDATA[select e from ElectricGuitar e where e.name = ? ]]> </query> <query name="ElectricGuitar.findAll"> <![CDATA[select e from ElectricGuitar e order by e.price]]> </query> </hibernate-mapping>
  • 46. GenericDao with Spring ? 仕組み – CRUD の実装は普通 – Finder の実装は AOP ? コード見てね
  • 48. 気になること ? そもそも O/R マッパって遅い – マッピングコストが高い ? 不必要な項目も取得してしまう ? 現状では解決は難しい
  • 49. 気になること ? 返値 List の Finder で OutOfMemory にな る – バッチ処理やファイルダウンロード処理であ りがち – レコードの数だけマッピングしてしまう ? そういう場合は Iterator を使おう – next() するごとにマッピング
  • 50. 気になること ? 複数レコードの更新?削除処理が遅い – CRUD しかないと、レコードの数だけ Update や Delete を繰り返し ? 専用メソッドを用意しよう – Update 文や Delete 文 – StoredProcedure
  • 51. 気になること ? キャッシュしたい – DAOでキャッシュ ? Hibernate は自動でやってくれる ? OSCache などで自分でキャッシュ
  • 52. 気になること ? パフォーマンス計測したい – AOP をつかうと楽 ? Bean 定義したクラスのメソッドの実行時間計測 public class TraceInterceptor implements MethodInterceptor { private static Log log = LogFactory.getLog(TraceInterceptor.class); public static ThreadLocal<String> local = new ThreadLocal<String>(); public Object invoke(MethodInvocation invocation) throws Throwable { String key = local.get(); if ( StringUtils.isEmpty(key) || "null".equals(key) ) { key = UUID.randomUUID().toString(); local.set(key); } log.debug("," + key + "," + System.currentTimeMillis() + "," + getInvocationDescription(invocation) + ",start"); Object value = invocation.proceed(); log.debug("," + key + "," + System.currentTimeMillis() + "," + getInvocationDescription(invocation) + ",end"); return value; }
  • 53. 気になること ? N+ 1問題 – 親レコードの数だけ子レコードを取得 する SQL が発行されてしまう ? 注文と注文商品みたいな関係 ? Lazy ロードで回避できるが??? – トランザクション注意! ? トランザクション終了前に子レコードを取 得する
  • 54. 気になること ? SQL インジェクション攻撃 ? プリペアドステートメント推奨 – SQL の構造が変わらない – とはいっても素のステートメントも使 いたい??? ? MySQL のクエリーキャッシュが効かない ? 使う場合きちんとエスケープする
  • 55. 気になること ? エンティティ露出問題 – ビュー層で必要とされるモデルとのミスマッチ ? ビュー層でマスターデータ参照したい – マスターにある項目を表示したいのだけど TransferObject には ID しかはいっていない??? – 詰め替え問題 ? DTO に誰がどこでつめなおすの? ? ビジネスロジック層で泣きながら DTO につめる ? 設計上の問題なので解決策があるわけではない
  • 56. Q&A
  • 57. ライセンスについて ? JSUG マスコットアイコン(本スライド左下)が残されている場合に限り、本作品 (またそれを元にした派生作品)の複製?頒布?表示?上演を認めます。 ? 非商用目的に限り、本作品(またそれを元にした派生作品)の複製?頒布?表示? 上演を認めます。 ? 本作品のライセンスを遵守する限り、派生作品を頒布することを許可します。