#author("2019-03-07T07:43:51+00:00","","")
#author("2020-09-10T02:31:03+00:00","","")
// 下階層用テンプレート
#topicpath
----
#contents

//ここにコンテンツを記述します。
IBMのサイトに[[HibernateとSpring AOPで、汎用性と型安全性を備えたDAOを作る>http://www.ibm.com/developerworks/jp/java/library/j-genericdao/]]という非常に興味深い記事を発見。これは業務アプリを作るときにいつも作成するDAOを効率よく作成する方法をまとめた記事です。通常DAOの作成って、エンティティごとに似たようなコーディングをしなくてはいけなくてひじょーに煩わしいのですが、GenericsとSpringを使うことによってこの面倒な作業から解放されます。目からウロコですね。


**やってみる [#ae529dac]
エンティティなどは[[Hibernate/Springを使ってトランザクション処理を記述する]]のものをそのまま流用します。

-DDL
  create table MKINO.USER_ATTR (
  USERID varchar2(100) not null,
  NAME varchar2(1000), primary key (USERID)
 );

-クラス図
#ref(classdiagram.png)



このクラス図で、UserDaoに当たる部分はエンティティごとに実装を書かなくてはいけないのですが、ココを楽することができるようになります。


***まずは基底クラスを作成 [#h38acb25]
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);
と記述しておくわけですね。




さて準備はできました。

***インタフェースIUserDaoを作成する [#w8c722bf]
次に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の実装クラスを作成する。 [#ff4e04f7]
次に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);
   }
 }

などとやるわけですが((これ、ホントめんどくさいっ))、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してあげればよいわけです。

***実行する [#nb7490a9]
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;
   }
 }




***これだけでも楽になったけど。 [#af494cd8]
これだけでも楽になったけど実は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に個別のメソッドを定義したい [#pf40a7d6]
ここまでのサンプルで、DAOにCRUDを実装する限りはDAOの実装クラスは必要ないということ、DAOのインタフェースもそれぞれCRUDを定義しないでもGenericsを使ってパラメータ化できることが分かりました。

さて通常DAOにはそのエンティティ固有の検索機能を実装したいですね。たとえば部分一致する検索などですね。それらはどうするのでしょうか。



----
この記事は
#vote(おもしろかった[61],そうでもない[8])
#vote(おもしろかった[62],そうでもない[8])
- 後半が難しい。。これだけややこしいと、人海戦術で開発してるときは無理かなあ?? -- [[きの]] &new{2007-05-09 (水) 11:35:38};
- GenericDaoHibernateImplのprivate Class<T> typeについてなんですが、コンストラクタの引数で初期化しなくても、this.type = (Class<T>) ( (ParameterizedType) getClass().getGenericSuperclass() ).getActualTypeArguments()[0]を引数なしのコンストラクタの中に書けば初期化できると、[Java Persistence with Hibernate]の本に書かれているらしいです -- [[しょうしょう]] &new{2008-10-14 (火) 23:34:33};
- GenericDaoHibernateImplのprivate Class<T> typeについてなんですが、コンストラクタの引数で初期化しなくても、this.type = (Class<T>) ( (ParameterizedType) getClass().getGenericSuperclass() ).getActualTypeArguments()[0]を引数なしのコンストラクタの中に書けば初期化できると、[Java Persistence with Hibernate]の本に書かれているらしいです -- [[しょうしょう]] &new{2008-10-15 (水) 00:59:05};

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS