<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    風(fēng)人園

    弱水三千,只取一瓢,便能解渴;佛法無邊,奉行一法,便能得益。
    隨筆 - 99, 文章 - 181, 評論 - 56, 引用 - 0
    數(shù)據(jù)加載中……

    Don’t repeat the DAO!

    譯者:Nicholas @ Nirvana Studio
    原文地址:http://www-128.ibm.com/developerworks/java/library/j-genericdao.html

    使用Hibernate和Spring AOP購建一個范型類型安全的DAO
    2006年五月12日

    在采用了Java 5的范型之后,要實(shí)現(xiàn)一個基于范型類型安全的數(shù)據(jù)訪問對象(DAO)就變得切實(shí)可行了。在這篇文章里,系統(tǒng)架構(gòu)師Per Mellqvist展示了一個基于Hibernate的范型DAO實(shí)現(xiàn)。然后將介紹如何使用Spring AOP的introduction為一個類增加一個類型安全的接口以便于執(zhí)行查詢。

    對于大多數(shù)開發(fā)者來說,在系統(tǒng)中為每一個DAO編寫幾乎一樣的代碼已經(jīng)成為了一種習(xí)慣。同時大家也都認(rèn)可這種重復(fù)就是“代碼的味道”,我們中的大多數(shù)已經(jīng)習(xí)慣如此。當(dāng)然也有另外的辦法。你可以使用很多ORM工具來避免代碼的重復(fù)編寫。舉個例子,用Hibernate,你可以簡單的使用session操作直接控制你的持久化領(lǐng)域?qū)ο蟆_@種方式的負(fù)面影響就是丟失了類型安全。

    為什么你的數(shù)據(jù)訪問代碼需要一個類型安全的接口?我認(rèn)為它減少了編程錯誤,提高了生產(chǎn)率,尤其是在使用現(xiàn)代高級IDE的時候。首先,一個類型安全的接口清晰的制定了哪些領(lǐng)域?qū)ο缶哂谐志没δ堋F浯危祟愋娃D(zhuǎn)換帶來的潛在問題。最后,它平衡了IDE的自動完成功能。使用自動完成功能是最快的方式來記住對于適當(dāng)?shù)念I(lǐng)域類哪些查詢是可用的。

    在這篇文章里,我將展示給大家如何避免一次次地重復(fù)編寫DAO代碼,但同時還收益于類型安全的接口。事實(shí)上,所有內(nèi)需要編寫的是為新的DAO編寫一個Hibernate映射文件,一個POJO的Java接口,并且10行Spring配置文件。

    DAO實(shí)現(xiàn)

    DAO模式對于任何Java開發(fā)人員來說都是耳熟能詳?shù)摹_@個模式的實(shí)現(xiàn)相當(dāng)多,所以讓我們仔細(xì)推敲一下我這篇文章里面對于DAO實(shí)現(xiàn)的一些假設(shè):

    • 所有系統(tǒng)中的數(shù)據(jù)庫訪問都是通過DAO來完成封裝
    • 每一個DAO實(shí)例對一個主要的領(lǐng)域?qū)ο蠡蛘邔?shí)體負(fù)責(zé)。如果一個領(lǐng)域?qū)ο缶哂歇?dú)立的生命周期,那么它需要具有自己的DAO。
    • DAO具有CRUD操作
    • DAO可以允許基于criteria方式的查詢而不僅僅是通過主鍵查詢。我將這些成為finder方法或者finders。這個finder的返回值通常是DAO所負(fù)責(zé)的領(lǐng)域?qū)ο蟮募稀?

    范型DAO接口

    范型DAO的基礎(chǔ)就是CRUD操作。下面的接口定義了范型DAO的方法:

    						public
    						interface GenericDao <T, PK extends Serializable> {
    ?
        /** Persist the newInstance object into database */
        PK create(T newInstance);
    ?
        /** Retrieve an object that was previously persisted to the database using
         *   the indicated id as primary key
         */
        T read(PK id);
    ?
        /** Save changes made to a persistent object.  */void update(T transientObject);
    ?
        /** Remove an object from persistent storage in the database */void delete(T persistentObject);
    }

    實(shí)現(xiàn)這個接口

    使用Hibernate實(shí)現(xiàn)上面的接口是非常簡單的。也就是調(diào)用一下Hibernate的方法和增加一些類型轉(zhuǎn)換。Spring負(fù)責(zé)session和transaction管理。

    						public
    						class GenericDaoHibernateImpl <T, PK extends Serializable>
        implements GenericDao<T, PK>, FinderExecutor {private Class<T> type;
    ?
        public GenericDaoHibernateImpl(Class<T> type){this.type = type;
        }
    ?
        public PK create(T o){return(PK) getSession().save(o);
        }
    ?
        public T read(PK id){return(T) getSession().get(type, id);
        }
    ?
        publicvoid update(T o){
            getSession().update(o);
        }
    ?
        publicvoid delete(T o){
            getSession().delete(o);
        }
    ?
        // Not showing implementations of getSession() and setSessionFactory()}

    Spring 配置

    最后,Spring配置,我創(chuàng)建了一個GenericDaoHibernateImpl的實(shí)例。GenericDaoHibernateImpl的構(gòu)造器必須被告知領(lǐng)域?qū)ο蟮念愋停@樣DAO實(shí)例才能為之負(fù)責(zé)。這個同樣需要Hibernate運(yùn)行時知道這個對象的類型。下面的代碼中,我將領(lǐng)域類Person傳遞給構(gòu)造器并且將Hibernate的session工廠作為一個參數(shù)用來實(shí)例化DAO:

    						
    								<bean
    								id="personDao"class="genericdao.impl.GenericDaoHibernateImpl">
    						
    								<constructor-arg>
    						
    						
    								<value>
    						genericdaotest.domain.Person</value></constructor-arg><propertyname="sessionFactory"><refbean="sessionFactory"/></property></bean>

    可用的范型DAO

    我還沒有全部完成,但我現(xiàn)在已經(jīng)有了一個可供作的代碼。下面的代碼展示了范型DAO如何使用:

    						public
    						void someMethodCreatingAPerson(){
        ...
        GenericDao dao = (GenericDao)
         beanFactory.getBean("personDao"); // This should normally be injected
    ?
        Person p = new Person("Per", 90);
        dao.create(p);
    }

    這時候,我有一個范型DAO有能力進(jìn)行類型安全的CRUD操作。同時也有理由編寫GenericDaoHibernateImpl的子類來為每個領(lǐng)域?qū)ο笤黾硬樵児δ堋5沁@篇文章的主旨在于展示如何完成這項(xiàng)功能而不是為每個查詢編寫明確的代碼,然而,我將會使用多個工具來介紹DAO的查詢,這就是Spring AOP和Hibernate命名查詢。

    Spring AOP介紹

    你可以使用Spring AOP提供的introduction功能將一個現(xiàn)存的對象包裝到一個代理里面來增加新的功能,定義它需要實(shí)現(xiàn)的新接口,并且將之前所有不支持的方法委派到一個處理機(jī)。在我的DAO實(shí)現(xiàn)里面,我用introduction將一定數(shù)量的finder方法增加到現(xiàn)存的范型DAO類里面。因?yàn)閒inder方法針對特定的領(lǐng)域?qū)ο螅运鼈儽粦?yīng)用到表明接口的范型DAO中。

    						
    								<bean
    								id="finderIntroductionAdvisor"class="genericdao.impl.FinderIntroductionAdvisor"/>
    ?
    <beanid="abstractDaoTarget"class="genericdao.impl.GenericDaoHibernateImpl"abstract="true"><propertyname="sessionFactory"><refbean="sessionFactory"/></property></bean>
    ?
    <beanid="abstractDao"class="org.springframework.aop.framework.ProxyFactoryBean"abstract="true"><propertyname="interceptorNames"><list><value>finderIntroductionAdvisor</value></list></property></bean>

    在上面的配置中,我定義了三個Spring bean,第一個bean,F(xiàn)inderIntroductionAdvisor,處理那些introduce到DAO中但是不屬于GenericDaoHibernateImpl類的方法。一會我再介紹Advisor bean的詳細(xì)情況。

    第二個bean定義為“abstract”。在Spring中,這個bean可以被其他bean重用但是它自己不會被實(shí)例化。不同于抽象屬性,bean的定義簡單的指出了我需要一個GenericDaoHibernateImpl的實(shí)例同時需要一個SessionFactory的引用。注意GenericDaoHibernateImpl類只定義了一個構(gòu)造器接受領(lǐng)域類作為參數(shù)。因?yàn)檫@個bean是抽象的,我可以無限次的重用并且設(shè)定合適的領(lǐng)域類。

    最后,第三個,也是最有意思的是bean將GenericDaoHibernateImpl的實(shí)例包裝進(jìn)了一個代理,給予了它執(zhí)行finder方法的能力。這個bean定義同樣是抽象的并且沒有指定任何接口。這個接口不同于任何具體的實(shí)例。

    擴(kuò)展通用DAO

    每個DAO的接口,都是基于GenericDAO接口的。我需要將為特定的領(lǐng)域類適配接口并且將其擴(kuò)展包含我的finder方法。

    						public
    						interface PersonDao extends GenericDao<Person, Long> {
        List<Person> findByName(String name);
    }

    上面的代碼清晰的展示了通過用戶名查找Person對象列表。所需的Java實(shí)現(xiàn)類不需要包含任何的更新操作,因?yàn)檫@些已經(jīng)包含在了通用DAO里。

    配置PersonDao

    因?yàn)镾pring配置依賴之前的那些抽象bean,所以它變得很緊湊。我需要指定DAO負(fù)責(zé)的領(lǐng)域類,并且我需要告訴Spring我這個DAO需要實(shí)現(xiàn)的接口。

    						
    								<bean
    								id="personDao"parent="abstractDao">
    						
    								<property
    								name="proxyInterfaces">
    						
    								<value>
    						genericdaotest.dao.PersonDao</value></property><propertyname="target"><beanparent="abstractDaoTarget"><constructor-arg><value>genericdaotest.domain.Person</value></constructor-arg></bean></property></bean>

    你可以這樣使用:

    						public
    						void someMethodCreatingAPerson(){
        ...
        PersonDao dao = (PersonDao)
         beanFactory.getBean("personDao"); // This should normally be injected
    ?
        Person p = new Person("Per", 90);
        dao.create(p);
    ?
        List<Person> result = dao.findByName("Per"); // Runtime exception}

    上面的代碼是使用類型安全接口PersonDao的一種正確途徑,但是DAO的實(shí)現(xiàn)并沒有完成。當(dāng)調(diào)用findByName()的時候?qū)е铝艘粋€運(yùn)行時異常。這個問題是我還沒有findByName()。剩下的工作就是指定查詢語句。要完成這個,我使用Hibernate命名查詢。

    Hibernate命名查詢

    使用Hibernate,你可以定義任何HQL查詢在映射文件里,并且給它一個名字。你可以在之后的代碼里面方便的通過名字引用這個查詢。這么做的一個優(yōu)點(diǎn)就是能夠在部署的時候調(diào)節(jié)查詢而不需要改變代碼。正如你一會將看到的,另一個好處就是實(shí)現(xiàn)一個“完整”的DAO而不需要編寫任何Java實(shí)現(xiàn)代碼。

    						
    								<hibernate-mapping
    								package="genericdaotest.domain">
    						
    								<class
    								name="Person">
    						
    								<id
    								name="id">
    						
    								<generator
    								class="native"/>
    						
    								</id>
    						
    						
    								<property
    								name="name"/>
    						
    								<property
    								name="weight"/>
    						
    								</class>
    						
    ?
         <queryname="Person.findByName"><![CDATA[select p from Person p where p.name = ? ]]></query></hibernate-mapping>

    上面的代碼定義了領(lǐng)域類Person的Hibernate映射文件,有兩個屬性:name和weight。Person是一個具有上面屬性的簡單的POJO。這個文件同時包含了一個查詢,通過提供的name屬性從數(shù)據(jù)庫查找Person實(shí)例。Hibernate為命名查詢提供了不真實(shí)的命名空間功能。為了便于討論,我將所有的查詢名字的前綴變成領(lǐng)域類的的名稱。在現(xiàn)實(shí)場景中,使用完整的類名,包含包名,是一個更好的主意。

    總覽

    你已經(jīng)看到了為任何領(lǐng)域?qū)ο髣?chuàng)建并配置DAO的所需步驟了。這三個簡單的步驟就是:

    1. 定義一個接口繼承GenericDao并且包含任何所需的finder方法
    2. 在映射文件中為每個領(lǐng)域類的finder方法增加一個命名查詢。
    3. 為DAO增加10行Spring配置

    可重用的DAO類

    Spring advisor和interceptor的功能比較瑣碎,事實(shí)上他們的工作都引用回了GenericDaoHibernateImpl類。所有帶有“find”開頭的方法都被傳遞給DAO的單一方法executeFinder()。

    						public
    						class FinderIntroductionAdvisor extends DefaultIntroductionAdvisor {public FinderIntroductionAdvisor(){super(new FinderIntroductionInterceptor());
        }}
    ?
    publicclass FinderIntroductionInterceptor implements IntroductionInterceptor {
    ?
        publicObject invoke(MethodInvocation methodInvocation)throwsThrowable{
    ?
            FinderExecutor genericDao = (FinderExecutor) methodInvocation.getThis();
    ?
            String methodName = methodInvocation.getMethod().getName();
            if(methodName.startsWith("find")){Object[] arguments = methodInvocation.getArguments();
                return genericDao.executeFinder(methodInvocation.getMethod(), arguments);
            }else{return methodInvocation.proceed();
            }}
    ?
        publicboolean implementsInterface(Class intf){return intf.isInterface() && FinderExecutor.class.isAssignableFrom(intf);
        }}

    executeFinder() 方法

    上面的代碼唯一缺的就是executeFinder的實(shí)現(xiàn)。這個代碼觀察被調(diào)用的類的名字和方法,并且將他們與Hibernate的查詢名相匹配。你可以使用一個FinderNamingStrategy來激活其他方式的命名查詢。默認(rèn)的實(shí)現(xiàn)查找一個名為“ClassName.methodName”的查詢,ClassName是除包名之外的類名。

    						public List<T> executeFinder(Method method, finalObject[] queryArgs){finalString queryName = queryNameFromMethod(method);
         finalQuery namedQuery = getSession().getNamedQuery(queryName);
         String[] namedParameters = namedQuery.getNamedParameters();
         for(int i = 0; i < queryArgs.length; i++){Object arg = queryArgs[i];
                 Type argType =  namedQuery.setParameter(i, arg);
          }return(List<T>) namedQuery.list();
    }
    ?
    publicString queryNameFromMethod(Method finderMethod){return type.getSimpleName() + "." + finderMethod.getName();
    }

    總結(jié)

    在Java 5之前,Java語言并不支持代碼同時具有類型安全和范性的特性;你不得不二者選一。在這篇文章里,你可以看到使用Java 5范型支持并且結(jié)合Spring和Hibernate(和AOP)一起來提高生產(chǎn)力。一個范型類型安全的DAO類非常容易編寫,所有你需要做的就是一個接口,一些命名查詢,并且10行Spring配置,并且可以極大的減少錯誤,同時節(jié)省時間。

    posted on 2007-03-05 11:51 風(fēng)人園 閱讀(197) 評論(0)  編輯  收藏 所屬分類: DAO

    主站蜘蛛池模板: 无遮免费网站在线入口| 99久久国产精品免费一区二区| 91免费播放人人爽人人快乐| 337P日本欧洲亚洲大胆精品| 四虎在线免费视频| 亚洲精品免费在线| 国产桃色在线成免费视频| 亚洲婷婷综合色高清在线| 韩国免费一级成人毛片| 涩涩色中文综合亚洲| 日韩a在线观看免费观看| 爱情岛论坛免费视频| 国产啪亚洲国产精品无码| AAA日本高清在线播放免费观看| 亚洲av无码一区二区三区乱子伦 | 女人18毛片a级毛片免费| 亚洲综合欧美色五月俺也去| 免费无码看av的网站| 国产一区二区三区亚洲综合| 亚洲精品无码久久毛片| 国产色无码精品视频免费| 久久丫精品国产亚洲av| 毛片免费在线播放| 特级毛片A级毛片100免费播放| 国产午夜亚洲精品午夜鲁丝片| 永久免费A∨片在线观看| 亚洲噜噜噜噜噜影院在线播放| 免费无码又爽又高潮视频| 一级做a爱过程免费视频高清| 亚洲人成影院在线| 女人张开腿给人桶免费视频 | 亚洲va国产va天堂va久久| 老司机在线免费视频| 杨幂最新免费特级毛片| 无码专区—VA亚洲V天堂| 在线免费观看一区二区三区| xvideos永久免费入口| 亚洲国产精品综合久久久| www国产亚洲精品久久久日本| 国产精品视频白浆免费视频| 亚洲欧美成人综合久久久|