| 
 
| 
 
 
 
 
 
 
| Blog信息 |  
| blog名称:日志总数:1304
 评论数量:2242
 留言数量:5
 访问次数:7644760
 建立时间:2006年5月29日
 |   
 
 |  | 
 
|  [Hibernate]use AOP to simplify hibernateTemplate (Hibernate3) 软件技术
 lhwork 发表于 2007/1/23 12:08:54  |  
| 
| Spring ORM 替 Hibernate 的 API (Session and Query) 做了一番修整,也就是 HibernateTemplate ,它最主要的目的是 resource 控管及 exception 的轉換。有了這個 persistence layer 的 code 就可以大量的減少並且降低出錯的機會。然而這個 API 卻抹剎了 Hibernate API 的簡單易用優點,舉例來說,如果藉由 hibernateTemplate 控制 Hibernate Session (ex. Criteria),唯一的做法就是寫很難看的 Callback:public MyData findByNo(final String no) { final String hql = " select data from MyData data "   + " where data.no = :no ";     HibernateCallback hc = new HibernateCallback() {  public Object doInHibernate(Session session) {   Query q = session.createQuery(hql);   q.setString("no", no);   return q.uniqueResult();  } };       return (MyData) hibernateTemplate.execute(hc);}inner class + final.... 有夠難看。下面要介紹的是,利用 Spring AOP 的 AutoProxy 的功能,自動將 DAO 的所有 method 用 HibernateCallback 包起來。如此一來,便可以自由在 DAO 中自由存取 Hibernate Session 了:總共需要三個 class:第一個是 Advice,也就是將所有 method 都先用 HibernateCallback 包起來,然後將 Hibernate Session 放入 HibernateDAOWarpper 裡。public class HibernateDAOWrapperMethodInterceptor                                       implements MethodInterceptor {    private HibernateTemplate hibernateTemplate;    public Object invoke(final MethodInvocation methodInvocation)            throws Throwable {        try {            return hibernateTemplate.execute(new HibernateCallback() {                public Object doInHibernate(Session session)                        throws HibernateException, SQLException {                    HibernateDAOWrapper.currentSession.set(session);                    try {                        return methodInvocation.proceed();                    } catch (Throwable e) {                        if (e instanceof HibernateException) {                            throw (HibernateException) e;                        } else if (e instanceof SQLException) {                            throw (SQLException) e;                        } else {                            //must re-throw as RuntimeException, and let                            //hibernateTemplate deal with resource management                            throw new ExceptionCarrier(e);                        }                    }                }            });        } catch (ExceptionCarrier e) {            throw e.throwable;        }    }    private static class ExceptionCarrier extends RuntimeException {        private Throwable throwable;        public ExceptionCarrier(Throwable t) {            this.throwable = t;        }    }    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {        this.hibernateTemplate = hibernateTemplate;    }}接下來就是 AutoProxyCreator,當它遇到 bean 的 class 為 HibernateDAOWrapper 的 subclass 時,它就會自動替該 bean 做 proxy。public class HibernateDAOWrapperAutoProxyCreator extends        AbstractAutoProxyCreator {    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass,            String beanName, TargetSource customTargetSource)            throws BeansException {        if (HibernateDAOWrapper.class.isAssignableFrom(beanClass)) {            return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;        } else {            return DO_NOT_PROXY;        }    }}有了這個,再搭配上面的 Advice,我們就可以將所有 HibernateDAOWrapper 的 subclass 的所有 method 都用 HibernateCallback 包起來。Spring 的設定如下:<bean id="hibernateDAOWrapperAutoProxyCreator" class="org.bioinfo.util.spring.dao.HibernateDAOWrapperAutoProxyCreator"> <property name="interceptorNames">  <list>   <value>hibernateDAOWrapperMethodInterceptor</value>  </list> </property> <!-- for use CGlib to create proxy,        (default to false, which will use Proxy if it detects interface presence) --> <property name="proxyTargetClass">       <value>true</value> </property></bean>     <bean id="hibernateDAOWrapperMethodInterceptor" class="org.bioinfo.util.spring.dao.HibernateDAOWrapperMethodInterceptor"> <property name="hibernateTemplate">  <ref local="hibernateTemplate"/> </property></bean>這個只要設一次就夠了。接下來我們再來看看主角:/** * A special HibernateTemplate wrapper for DAO. You need to do: *  * (1) Extends this class and use getSession() to obtain Hibernate3 Session *  * <code> * public class YourDAO extends HibernateDAOWrapper { *  *     public MyProject findByProjectCode(String code) { *         String hql = "select prj from MyProject prj where prj.code = :code"; *  *         return (MyProject) getSession().createQuery(hql) *                      .setString("code",code").uniqueResult(); *     } * } * </code> *  * (2) At your spring *.xml, just write one line: *  * <code> * <bean id="yourDAO" class="some.thing.YourDAO"></bean> * </code> *  * done !! * @author ingram *   */public abstract class HibernateDAOWrapper {    final static ThreadLocal currentSession = new ThreadLocal();    protected final Session getSession() {        return (Session) currentSession.get();    }}呵,小的可憐,它的功能就是提供 getSession() 的 method 給 subclass 的 DAO 而已。這個 session 來自於 Advice,並且放在 ThreadLocal裡,因此是 thread-safe 的。使用方式就如上面的 javadoc 所說,將 DAO 直接 subclass 這個 Wrapper,並在 spring 裡定義即可。這樣一來 DAO 就可以快快樂樂的用 Hibernate API 啦,爽!(註:僅適用 Hibernate3,於 Hibernate 3.0.3 + Spring 1.2 final 測試 ) |  
 |  
 
 
 
 |