springでdao 20070413

57
Spring で DAO ででで ででででででででででででで () でででで ででででででで ()

Upload: funato-takashi

Post on 25-May-2015

1.473 views

Category:

Documents


0 download

DESCRIPTION

大昔の資料が出てきた

TRANSCRIPT

Page 1: Springでdao 20070413

Spring で DAO

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

Page 2: Springでdao 20070413

目的

• Spring を使って DAO を使う方法を理解する• DAO を使う場合の注意点を理解する• 皆がどんな風に DAO を使っているのか共有する

Page 3: Springでdao 20070413

アジェンダ

• DAO とは• DAO の設計• Spring で DAO

– 実装– 設定– テスト

• GenericDao with Spring

• 気になること

Page 4: Springでdao 20070413

DAO とは

Page 5: Springでdao 20070413

DAO とは

• Data Access Object の略– J2EE パターンで

紹介– 永続化層におい

て様々なデータソースを抽象化する

– 今回はRDB ( JDBC)前提

http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html

Page 6: Springでdao 20070413

DAO とは

• メリット– SQL が集約されるためメンテナンス性が高い– TransferObject になるので Java コードから

扱いやすい。型重要• デメリット

– 他層からデータアクセスの柔軟性が失われる– TransferObject を作成するコストが高い

Page 7: Springでdao 20070413

DAO の設計

Page 8: Springでdao 20070413

DAO の設計

• DAO のインターフェース設計– CRUD + Finder メソッド– パラメタオブジェクトを利用する– Tiger 便利

• TypeSafeEnum• Generics

Page 9: Springでdao 20070413

DAO の設計

• CRUD + Finder メソッド– CRUD ( Create 、 Read 、 Update 、 Delet

e )は、 TransferObject をまるごとやりとり• Read は ID による検索

– ID 以外の検索は Finder メソッドとして、ある程度の目的別に用意

Page 10: Springでdao 20070413

DAO の設計

• パラメタオブジェクトを利用する– DAO の Finder メソッドにおいて、パラメタ

が増えるたびにインターフェースが変わるのはいや

– そこでパラメタオブジェクトとして抽象化• パタメタが増えても、 DAO のインターフェースに

影響を与えないpublic interface BasicDao { List findByCriteria(BasicFindCriteria criteria);}

public class BasicFindCriteria { private String someKey;}

Page 11: Springでdao 20070413

DAO の設計

• Tiger 便利( JavaSE5 )– 型重要– TypeSafeEnum

– Genericspublic interface BasicDao { List<BasicTransferObject> findByCriteria(BasicFindCriteria criteria);}

public enum AnyEnum { type1, type2}

Page 12: Springでdao 20070413

Spring で DAO

Page 13: Springでdao 20070413

Spring で DAO

• 実装のコツ– 基本は ORM を使う– Spring の ORM サポートクラスを使う

• 設定のコツ– AOP でトランザクション設定

• テストのコツ– Spring で用意されたテストサポートクラスを使う– DAO を使うクラスをテストする場合はモックを

使う

Page 14: Springでdao 20070413

Spring で DAO – 実装

Page 15: Springでdao 20070413

Spring で DAO – 実装

• 基本は ORM を使う– とりあえず便利– でも無理に使うことはない

• Spring の ORM サポートクラスを使う– Exception の違いを吸収

• Hibernate→HibernateException• iBATIS→SQLException

– try catch を書かなくて済むのでコードがすっきり– 例外で処理を判定するコードは書かない

• org.hibernate.NonUniqueObjectException で判定するなど

Page 16: Springでdao 20070413

Spring で DAO – 実装

• 実装の流れ– TransferObject を作る– DAO のインターフェースを作る– DAO の実装をする– マッピングファイル書く– Spring に Bean 定義する

• ORM で実装サンプル– 今回は Hibernate 、 iBATIS そして・・・

Page 17: Springでdao 20070413

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))

Page 18: Springでdao 20070413

Spring で DAO – 実装

• TransferObject を作るpublic class ElectricGuitar {

private String id;

private String name;

private ManufactureEnum manufacture;

private Date craftedDate;

private Integer price;・・・

Java5 のEnum

Page 19: Springでdao 20070413

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);

}

Page 20: Springでdao 20070413

Spring で DAO – 実装

• Hibernate のサンプルpublic class ElectricGuitarHibernateDaoImpl extends

HibernateDaoSupport implements ElectricGuitarDao {

public List<ElectricGuitar> findAll() { return getHibernateTemplate().loadAll(ElectricGuitar.class); }

・・・

Page 21: Springでdao 20070413

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>

Page 22: Springでdao 20070413

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 を組み立てくれる

Page 23: Springでdao 20070413

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); }   ・   ・   ・

Page 24: Springでdao 20070413

Spring で DAO – 実装

• マッピングファイルを書く– iBATIS のマッピングファイル

<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>   ・   ・   ・

src/main/resources/ibatis/ElectricGuitar.sqlmap.xml

Page 25: Springでdao 20070413

Spring で DAO – 実装

• iBATIS の Spring 設定

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="classpath:ibatis/sqlmap-config.xml" /> <property name="dataSource" ref="dataSource" /> </bean> <bean id="electricGuitarDao" class="jp.springframework.vol2.dao.ibatis.ElectricGuitarIbatisDaoImpl"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean>

src/main/resources/context/applicationContextIbatis.xml

com.ibatis.sqlmap.client. SqlMapClient を組み立てくれる

Page 26: Springでdao 20070413

Spring で DAO – 実装

• Spring-S2DAO のサンプル– S2DAO はインターフェースのみで動作する

ため実装クラスはありません

Spring-S2DaoVia:http://d.hatena.ne.jp/n-ichimura/

Page 27: Springでdao 20070413

Spring で DAO – 実装

• S2DAO の設定– 基本的に設定レス– Java ソースコードにメタデータを記述するだ

け – メソッドを命名規則に合わせることで SQL 文

の記述が不要

Page 28: Springでdao 20070413

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 定義をしません

Page 29: Springでdao 20070413

Spring で DAO – 実装

• ORM サポートクラスは他にもいろいろ– org.springframework.orm パッケージ以下– Hibernate 、 iBATIS 、 TopLink 、 JPA 、 etc…

詳しくは

Page 30: Springでdao 20070413

Spring で DAO – 設定

Page 31: Springでdao 20070413

Spring で DAO – 設定• AOP でトランザクション設定

– トランザクション単位を考えようsrc/main/resources/context/applicationContext-transaction.xml

<aop:config> <aop:advisor pointcut="execution(* *..*MyBusinessLogic.*(..))"     advice-ref="txAdvice"/> </aop:config>

<tx:advice id="txAdvice"> <tx:attributes> <tx:method name="calculateAmount" read-only="true"/> </tx:attributes> </tx:advice>

Spring 2.Xから使える

ビジネスロジックにトランザクションをかけた場合その中の DAOのトランザクションはすべて同一になる

Page 32: Springでdao 20070413

Spring で DAO – テスト

Page 33: Springでdao 20070413

• Spring で用意されたテストサポートクラスを使う( org.springframework.test.AbstractTransactionalDataSourceSpringContextTests )– Spring のコンテナを自動で初期化してくれる

– Setter があれば自動で Inject してくれる

– 自動でトランザクションの開始&ロールバック– テスト用 SQL 実行メソッドがある

Spring で DAO – テスト

executeSqlScript("classpath:testdata.sql", false);

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

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

Page 34: Springでdao 20070413

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()); }       ・   ・

Page 35: Springでdao 20070413

Spring で DAO- テスト• DAO を使うクラスをテストする場

合はモックを使う– 圧倒的なパフォーマンスの差

• インテグレーションテストを行う際に威力を発揮

• MyBusinessLogicDaoTest

• MyBusinessLogicMockTest

– 作業の非同期化• DAO の実装なしでもビジネスロジッ

クのテストを書ける

Page 36: Springでdao 20070413

Spring で DAO – テスト

• モックを使った実装サンプル– ビジネスロジック( MyBusinessLogic )では、割

引入り合計金額計算( calcuateAmount )を行う

Page 37: Springでdao 20070413

Spring で DAO – テスト

• easymock ( http://www.easymock.org/)• 手順

1.モックを準備2.モックの動きを用意3.テストを実施4.モックの動きを確認

Page 38: Springでdao 20070413

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するだけ

Page 39: Springでdao 20070413

Spring で DAO – テスト

• 手順 2: モックの動きを用意

ElectricGuitar a = new ElectricGuitar();a.setManufacture(ManufactureEnum.gretsch);a.setPrice(100000);expect(mock.load("1")).andReturn(a);expectLastCall().times(2);

ElectricGuitar b = new ElectricGuitar();b.setManufacture(ManufactureEnum.fender);b.setPrice(200000);expect(mock.load("2")).andReturn(b);

replay(mock);

これを呼ばれると、 これが返るよ

それを 2回

これで用意完了

Page 40: Springでdao 20070413

Spring で DAO – テスト

• 手順 3: テストを実施– 普通にテストするだけ

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

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

Page 41: Springでdao 20070413

Spring で DAO – テスト

• 手順 4: モックの動きを確認– 用意した通りに動いたことを確認

verify(mock);

Spring で DAO – テスト

Page 42: Springでdao 20070413

GenericDao with Spring

Page 43: Springでdao 20070413

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 を利用

Page 44: Springでdao 20070413

GenericDao with Spring

• 実装サンプル– Generics を使うことで、型つきの CRUD メ

ソッドが準備される– Finder メソッドは、メソッド名と同じ query

を用意する

Page 45: Springでdao 20070413

GenericDao with Spring

• インターフェースとマッピングファイルだけpublic interface ElectricGuitarGenericDao

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>

Page 46: Springでdao 20070413

GenericDao with Spring

• 仕組み– CRUD の実装は普通– Finder の実装は AOP

• コード見てね

Page 47: Springでdao 20070413

気になること

Page 48: Springでdao 20070413

気になること

• そもそも O/R マッパって遅い– マッピングコストが高い

• 不必要な項目も取得してしまう• 現状では解決は難しい

Page 49: Springでdao 20070413

気になること

• 返値 List の Finder で OutOfMemory になる– バッチ処理やファイルダウンロード処理であ

りがち– レコードの数だけマッピングしてしまう

• そういう場合は Iterator を使おう– next() するごとにマッピング

Page 50: Springでdao 20070413

気になること

• 複数レコードの更新・削除処理が遅い– CRUD しかないと、レコードの数だけ Updat

e や Delete を繰り返し• 専用メソッドを用意しよう

– Update 文や Delete 文– StoredProcedure

Page 51: Springでdao 20070413

気になること

• キャッシュしたい– DAOでキャッシュ

• Hibernate は自動でやってくれる• OSCache などで自分でキャッシュ

Page 52: Springでdao 20070413

気になること

• パフォーマンス計測したい– 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; }

Page 53: Springでdao 20070413

気になること

• N+ 1問題– 親レコードの数だけ子レコードを取得

する SQL が発行されてしまう• 注文と注文商品みたいな関係

• Lazy ロードで回避できるが・・・– トランザクション注意!

• トランザクション終了前に子レコードを取得する

Page 54: Springでdao 20070413

気になること

• SQL インジェクション攻撃• プリペアドステートメント推奨

– SQL の構造が変わらない– とはいっても素のステートメントも使

いたい・・・• MySQL のクエリーキャッシュが効かない• 使う場合きちんとエスケープする

Page 55: Springでdao 20070413

気になること

• エンティティ露出問題– ビュー層で必要とされるモデルとのミスマッチ

• ビュー層でマスターデータ参照したい– マスターにある項目を表示したいのだけど TransferObj

ect には ID しかはいっていない・・・

– 詰め替え問題• DTO に誰がどこでつめなおすの?• ビジネスロジック層で泣きながら DTO につめる

• 設計上の問題なので解決策があるわけではない

Page 56: Springでdao 20070413

Q&A

Page 57: Springでdao 20070413

ライセンスについて• JSUG マスコットアイコン(本スライド左下)が残されている場合に限り、本作品

(またそれを元にした派生作品)の複製・頒布・表示・上演を認めます。

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

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