Springて?dao 200704132. 目的
? 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 を使うクラスをテストする場合はモック
を使う
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…
詳しくは
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 のトランザクションはすべて同一になる
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 を利用
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>
49. 気になること
? 返値 List の Finder で OutOfMemory にな
る
– バッチ処理やファイルダウンロード処理であ
りがち
– レコードの数だけマッピングしてしまう
? そういう場合は Iterator を使おう
– next() するごとにマッピング
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 につめる
? 設計上の問題なので解決策があるわけではない
57. ライセンスについて
? JSUG マスコットアイコン(本スライド左下)が残されている場合に限り、本作品
(またそれを元にした派生作品)の複製?頒布?表示?上演を認めます。
? 非商用目的に限り、本作品(またそれを元にした派生作品)の複製?頒布?表示?
上演を認めます。
? 本作品のライセンスを遵守する限り、派生作品を頒布することを許可します。