Blog信息 |
blog名称: 日志总数:1304 评论数量:2242 留言数量:5 访问次数:7608082 建立时间: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 測試 ) |
|
|