IBMのサイトにHibernateとSpring AOPで、汎用性と型安全性を備えたDAOを作るという非常に興味深い記事を発見。これは業務アプリを作るときにいつも作成するDAOを効率よく作成する方法をまとめた記事です。通常DAOの作成って、エンティティごとに似たようなコーディングをしなくてはいけなくてひじょーに煩わしいのですが、GenericsとSpringを使うことによってこの面倒な作業から解放されます。目からウロコですね。
エンティティなどはHibernate/Springを使ってトランザクション処理を記述するのものをそのまま流用します。
create table MKINO.USER_ATTR ( USERID varchar2(100) not null, NAME varchar2(1000), primary key (USERID) );
このクラス図で、UserDao?に当たる部分はエンティティごとに実装を書かなくてはいけないのですが、ココを楽することができるようになります。
UserDao?の既定となるクラスを作成します。
import java.io.Serializable; public interface GenericDao<T, PK extends Serializable> { PK create(T newInstance); T read(PK id); void update(T transientObject); void delete(T persistentObject); }
エンティティに対するCRUDのインタフェースを定義しました。Genericsを使ってプライマリキーとエンティティをパラメータ化しているのがポイントです。
次に実装クラスをHibernateとSpringを使って実装します。
import java.io.Serializable; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public class GenericDaoHibernateImpl<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> { private Class<T> type; public GenericDaoHibernateImpl(Class<T> type) { this.type = type; } public PK create(T o) { return (PK) getHibernateTemplate().save(o); } public T read(PK id) { return (T) getHibernateTemplate().get(type, id); } public void update(T o) { getHibernateTemplate().update(o); } public void delete(T o) { getHibernateTemplate().delete(o); } }
実装クラスでも型はパラメータ化されたままになってます。またサブクラスで
return (T) getHibernateTemplate().get(type, id);
の部分が
return (Hoge) getHibernateTemplate().get(Hoge.class, id);
とならなくてはならないのでClassクラスを引数に取るコンストラクタ
public GenericDaoHibernateImpl(Class<T> type) { this.type = type; }
が定義されています。んでメソッドは
return (T) getHibernateTemplate().get(type, id);
と記述しておくわけですね。
さて準備はできました。
次にUserDao?のインタフェースIUserDao?を作成します。これはココのやり方でなくても作成するいわゆるDAOのインタフェースです。通常は
import nu.mine.kino.entity.UserAttr; public interface IUserDao { String create(UserAttr newInstance); void delete(UserAttr persistentObject); UserAttr read(String id); void update(UserAttr transientObject); }
なんてのを定義するのですが、上のGenericDao?インタフェースを作成したので
import nu.mine.kino.entity.UserAttr; public interface IUserDao extends GenericDao<UserAttr, String> { }
とテンプレート化された型を指定するだけでOKとなります。ここはエンティティごとに引数や戻り値をちょこちょこ修正して作成するというインタフェースなので、ココの記述が楽になるのはすごくうれしいですね。
次にIUserDao?の実装を作成します。ココも通常は
import java.util.List; import nu.mine.kino.entity.UserAttr; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; /** * @author Masatomi KINO * @version $Revision: 1.2 $ */ public class UserDao extends HibernateDaoSupport implements IUserDao { public String create(UserAttr newInstance) { return (String) getHibernateTemplate().save(newInstance); } public void delete(UserAttr persistentObject) { getHibernateTemplate().delete(persistentObject); } public UserAttr read(String id) { return (UserAttr) getHibernateTemplate().get(UserAttr.class, id); } public void update(UserAttr transientObject) { getHibernateTemplate().update(transientObject); } }
などとやるわけですが*1、GenericDaoHibernateImpl?があるおかげで
import java.util.List; import nu.mine.kino.entity.UserAttr; /** * とりあえず、IUserDaoの実装クラスとして作成したDAOクラス。 * 実際は、IUserDao (とextends GenericDao<UserAttr,String>) * の指定だけで十分なので、このクラスは必要ない。 * * @author Masatomi KINO * @version $Revision: 1.1 $ */ public class UserDao extends GenericDaoHibernateImpl<UserAttr, String> implements IUserDao { public UserDao(Class<UserAttr> type) { super(type); } }
となるだけです。
さてSpringのBean定義は以下のようになります。
<beans default-autowire="no" default-lazy-init="false" default-dependency-check="none"> <bean id="genericUserBL" class="nu.mine.kino.genericbl.UserBL"> <property name="dao"> <ref bean="userGenericDao"/> </property> </bean> <bean id="userGenericDao" class="nu.mine.kino.genericdao.UserDao"> <constructor-arg> <value>nu.mine.kino.entity.UserAttr</value> </constructor-arg> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean> </beans>
このようにBLにInjectionしてあげればよいわけです。
BLは今まで通り作ればよいです。
public interface IUserBL { String create(UserAttr newInstance); void delete(UserAttr persistentObject); UserAttr read(String id); void update(UserAttr transientObject); }
と実装クラスは
public class UserBL implements IUserBL { private IUserDao dao; public String create(UserAttr newInstance) {return dao.create(newInstance);} public void delete(UserAttr persistentObject) {dao.delete(persistentObject);} public UserAttr read(String id) {return dao.read(id);} public void update(UserAttr transientObject) {dao.update(transientObject);} public void setDao(IUserDao dao) {this.dao = dao;} }
となります。実行するメインクラスは以下の通り。
public class GenericSpringMain { public static void main(String[] args) throws IOException { DOMConfigurator.configure("log4j.xml"); ApplicationContext context = new FileSystemXmlApplicationContext( new String[] { "beans.xml", "hibernate-spring_test.xml" }); String name = getUniqueKey(); IUserBL bl = (IUserBL) context.getBean("genericUserBL"); bl.someMethod();//なんかBL } /** * PKを生成したいだけ。本質的な意味ナシ */ private static String getUniqueKey() throws IOException { File temp = File.createTempFile("abc", ""); temp.deleteOnExit(); String name = temp.getName(); return name; } }
これだけでも楽になったけど実はUserDao?の部分はSpringを使ってるので作成する必要はないみたいです。ようするにBean定義を
<bean id="userGenericDao" class="nu.mine.kino.genericdao.GenericDaoHibernateImpl"> <-変更 <constructor-arg> <value>nu.mine.kino.entity.UserAttr</value> </constructor-arg> <property name="sessionFactory"> <ref bean="sessionFactory"/> </property> </bean>
に変更し、BLを
public class UserBL implements IUserBL { private GenericDao<UserAttr, String> dao; //IUserDaoをGenericDaoに変更 //IUserDaoをGenericDaoに変更 public void setDao(GenericDao<UserAttr, String> dao) { this.dao = dao; } // あとは同じ }
と変更すればOKです。もはやUserDao?/IUserDao?は必要ナシです。CRUDだけなら汎用的なDAOとSpringのBean指定だけで十分なんですね。
ここまでのサンプルで、DAOにCRUDを実装する限りはDAOの実装クラスは必要ないということ、DAOのインタフェースもそれぞれCRUDを定義しないでもGenericsを使ってパラメータ化できることが分かりました。
さて通常DAOにはそのエンティティ固有の検索機能を実装したいですね。たとえば部分一致する検索などですね。それらはどうするのでしょうか。
この記事は
現在のアクセス:56524