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

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

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

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    使用Annotation設計持久層

    Posted on 2007-02-06 12:24 dennis 閱讀(1002) 評論(0)  編輯  收藏 所屬分類: 模式與架構
    ?這篇文章的想法來自于過去的兩篇文章:《設計自己的MVC框架》《設計模式之事務處理》
    鏈接:
    http://www.javaresearch.org/article/59935.htm
    http://www.javaresearch.org/article/59043.htm


    代碼下載同樣在www.126.com的郵箱里,用戶名 sharesources 密碼 javafans

    ??? 本文只是學習性質的文章,我一開始的想法就是修改《設計模式之事務處理》,提供Annotation來提供事務支持,支持到方法級別。通過引入一個 @Transaction標注,如果被此標注的方法將自動享受事務處理。目的是學習下Annotation和加深下對聲明式事務處理的理解。

    ??? Annotation是JDK5引入的新特性,現在越來越多的框架采用此特性來代替煩瑣的xml配置文件,比如hibernate,ejb3, spring等。對Annotation不了解,請閱讀IBM網站上的文章,還有推薦javaeye的Annotation專欄:http: //www.javaeye.com/subject/Annotation

    ??? 代碼的示例是一個簡單的用戶管理例子。

    ??? 首先,環境是mysql+jdk5+myeclipse5+tomcat5,在mysql中建立一張表adminusers:
    ??? create table adminusers(id int(10) auto_increment not null primary key,
    ???? name varchar(10) not null,
    ???? password varchar(10) not null,
    ???? user_type varchar(10));
    ??? 然后在tomcat下建立一個數據源,把代碼中的strutslet.xml拷貝到tomcat安裝目錄下的 /conf/Catalina/localhost目錄里,請自行修改文件中的數據庫用戶名和密碼,以及數據庫名稱。另外,把mysql的 jdbc驅動拷貝到tomcat安裝目錄下的common/lib目錄。這樣數據源就建好了。在web.xml中引用:

    ?? <resource-ref>
    ??? ??? <description>DB Connection</description>
    ??? ??? <res-ref-name>jdbc/test</res-ref-name>
    ??? ??? <res-type>javax.sql.DataSource</res-type>
    ??? ??? <res-auth>Container</res-auth>
    ??? </resource-ref>
    ???
    ??? 我的例子只是在《設計模式之事務處理》的基礎上改造的,在那篇文章里,我講解了自己對聲明式事務處理的理解,并利用動態代理實現了一個 TransactionWrapper(事務包裝器),通過業務代理工廠提供兩種版本的業務對象:經過事務包裝的和未經過事務包裝的。我們在默認情況下包裝業務對象中的所有方法,但實際情況是,業務對象中的很多方法不用跟數據庫打交道,它們根本不需要包裝在一個事務上下文中,這就引出了,我們為什么不提供一種方式來配置哪些方法需要事務控制而哪些并不需要?甚至提供事務隔離級別的聲明?很自然的想法就是提供一個配置文件,類似spring式的事務聲明。既然JDK5已經引入Annotation,相比于配置文件的煩瑣和容易出錯,我們定義一個@Transaction的annotation來提供此功能。

    ??? 看下Transaction.java的代碼:
    ??? package com.strutslet.db;

    ??? import java.lang.annotation.Documented;
    ??? import java.lang.annotation.ElementType;
    ??? import java.lang.annotation.Retention;
    ??? import java.lang.annotation.RetentionPolicy;
    ??? import java.lang.annotation.Target;
    ??? import java.sql.Connection;

    ??? @Target(ElementType.METHOD)
    ??? @Retention(RetentionPolicy.RUNTIME)
    ??? @Documented
    ??? public @interface Transaction {
    ?? ? ? //事務隔離級別,默認為read_committed
    ?????? public int level() default Connection.TRANSACTION_READ_COMMITTED??? ;
    ??? }

    @Transaction 標注只有一個屬性level,level表示事務的隔離級別,默認為Read_Committed(也是一般JDBC驅動的默認級別,JDBC驅動默認級別一般于數據庫的隔離級別一致)。 @Target(ElementType.METHOD)表示此標注作用于方法級別, @Retention(RetentionPolicy.RUNTIME)表示在運行時,此標注的信息將被加載進JVM并可以通過Annotation的 API讀取。我們在運行時讀取Annotation的信息,根據隔離級別和被標注的方法名決定是否將業務對象的方法加進事務控制。我們只要稍微修改下 TransactionWrapper:

    //TransactionWrapper.java
    package com.strutslet.db;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.SQLException;

    import com.strutslet.exception.SystemException;

    public class TransactionWrapper {

    ???
    ??? public static Object decorate(Object delegate) {
    ??? ??? return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
    ??? ??? ??? ??? delegate.getClass().getInterfaces(), new XAWrapperHandler(
    ??? ??? ??? ??? ??? ??? delegate));
    ??? }

    ??? static final class XAWrapperHandler implements InvocationHandler {
    ??? ??? private final Object delegate;

    ??? ??? XAWrapperHandler(Object delegate) {
    ??? ??? ??? // Cache the wrapped delegate, so we can pass method invocations
    ??? ??? ??? // to it.
    ??? ??? ??? this.delegate = delegate;
    ??? ??? }

    ??? ??? public Object invoke(Object proxy, Method method, Object[] args)
    ??? ??? ??? ??? throws Throwable {
    ??? ??? ??? Object result = null;
    ??? ??? ??? Connection con = ConnectionManager.getConnection();
    ??????????? //得到Transaction標注
    ??? ??? ??? Transaction transaction = method.getAnnotation(Transaction.class);

    ??? ??? ??? //如果不為空,說明代理對象調用的方法需要事務控制。
    ??? ??? ??? if (transaction != null) {
    ??? ??? ??? ??? // System.out.println("transaction.." + con.toString());
    ??? ??? ??? ??? // 得到事務隔離級別信息
    ??? ??? ??? ??? int level = transaction.level();
    ??? ??? ??? ??? try {
    ??? ??? ??? ??? ??? if (con.getAutoCommit())
    ??? ??? ??? ??? ??? ??? con.setAutoCommit(false);
    ??????????????????? //設置事務隔離級別
    ??? ??? ??? ??? ??? con.setTransactionIsolation(level);
    ??????????????????? //調用原始對象的業務方法
    ??? ??? ??? ??? ??? result = method.invoke(delegate, args);
    ??? ??? ??? ??? ??? con.commit();
    ??? ??? ??? ??? ??? con.setAutoCommit(true);
    ??? ??? ??? ??? } catch (SQLException se) {
    ??? ??? ??? ??? ??? // Rollback exception will be thrown by the invoke method
    ??? ??? ??? ??? ??? con.rollback();
    ??? ??? ??? ??? ??? con.setAutoCommit(true);
    ??? ??? ??? ??? ??? throw new SystemException(se);
    ??? ??? ??? ??? } catch (Exception e) {
    ??? ??? ??? ??? ??? con.rollback();
    ??? ??? ??? ??? ??? con.setAutoCommit(true);
    ??? ??? ??? ??? ??? throw new SystemException(e);
    ??? ??? ??? ??? }
    ??? ??? ??? } else {
    ??? ??? ??? ??? result = method.invoke(delegate, args);
    ??? ??? ??? }

    ??? ??? ??? return result;
    ??? ??? }
    ??? }
    }

    現在,看下我們的UserManager業務接口,請注意,我們是使用動態代理,只能代理接口,所以要把@Transaction標注是接口中的業務方法(與EJB3中的Remote,Local接口類似的道理):
    package com.strutslet.demo.service;

    import java.sql.SQLException;

    import com.strutslet.db.Transaction;
    import com.strutslet.demo.domain.AdminUser;

    public interface UserManager {
    ??? //查詢,不需要事務控制
    ??? public boolean checkUser(String name, String password) throws SQLException;

    ??? //新增一個用戶,需要事務控制,默認級別
    ??? @Transaction
    ??? public boolean addUser(AdminUser user) throws SQLException;

    }

    要把addUser改成其他事務隔離級別(比如oracle的serializable級別),稍微修改下:@Transaction(level=Connection.TRANSACTION_SERIALIZABLE)
    public boolean addUser(AdminUser user) throws SQLException;

    不準備詳細解釋例子的業務流程,不過是登錄和增加用戶兩個業務方法,看下就明白。閱讀本文前最好已經讀過開頭提過的兩篇文章。我相信代碼是最好的解釋:)
    主站蜘蛛池模板: 一区二区三区精品高清视频免费在线播放 | 污视频网站免费在线观看| ww在线观视频免费观看| 久久久久久久亚洲Av无码| 99精品免费观看| 精品亚洲成a人片在线观看少妇 | 久久久久久国产a免费观看黄色大片 | 国产亚洲精品看片在线观看| 一级毛片人与动免费观看 | 亚洲成年轻人电影网站www| 韩日电影在线播放免费版| 亚洲精品蜜桃久久久久久| a色毛片免费视频| 亚洲日本中文字幕| 在线观看H网址免费入口| 亚洲sss综合天堂久久久| 天天摸天天操免费播放小视频| 日韩国产欧美亚洲v片| 亚洲综合精品网站在线观看| 成人精品一区二区三区不卡免费看| 亚洲AV无码乱码国产麻豆| 57pao国产成视频免费播放| 亚洲AV一二三区成人影片| 国产免费AV片无码永久免费| 国产精品美女久久久免费| 久久久久亚洲av无码尤物| 99久久久国产精品免费无卡顿| 亚洲性无码AV中文字幕| 亚洲区小说区图片区| 99久久99久久免费精品小说| 国产精品亚洲综合五月天| 亚洲成A人片在线观看无码3D| 日韩a级无码免费视频| 亚洲视频无码高清在线| 亚洲人成无码久久电影网站| 日本高清免费观看| 亚洲狠狠婷婷综合久久蜜芽| 亚洲日本va中文字幕久久| 24小时免费直播在线观看| jizz18免费视频| 亚洲欧洲另类春色校园网站|