Springでdao 20070413

  • 1,162 views
Uploaded on

大昔の資料が出てきた

大昔の資料が出てきた

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,162
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
4
Comments
0
Likes
2

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Spring で DAO船戸隆(エーティーエルシステムズ)鈴木雄介(アークランプ)
  • 2. 目的• Spring を使って DAO を使う方法を理解する• DAO を使う場合の注意点を理解する• 皆がどんな風に DAO を使っているのか共有する
  • 3. アジェンダ• DAO とは• DAO の設計• Spring で DAO – 実装 – 設定 – テスト• GenericDao with Spring• 気になること
  • 4. DAO とは
  • 5. DAO とは• Data Access Object の略 – J2EE パターンで 紹介 – 永続化層におい て様々なデータ ソースを抽象化 する – 今回は RDB ( JDBC) 前提 http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
  • 6. DAO とは• メリット – SQL が集約されるためメンテナンス性が高い – TransferObject になるので Java コードから扱 いやすい。型重要• デメリット – 他層からデータアクセスの柔軟性が失われる – TransferObject を作成するコストが高い
  • 7. DAO の設計
  • 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); }
  • 12. Spring で DAO
  • 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-S2DaoVia: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);
  • 42. GenericDao with Spring
  • 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• コード見てね
  • 47. 気になること
  • 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 マスコットアイコン(本スライド左下)が残されている場合に限り、本作品 (またそれを元にした派生作品)の複製・頒布・表示・上演を認めます。• 非商用目的に限り、本作品(またそれを元にした派生作品)の複製・頒布・表示・ 上演を認めます。• 本作品のライセンスを遵守する限り、派生作品を頒布することを許可します。