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

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

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

    nighty

    折騰的年華
    posts - 37, comments - 143, trackbacks - 0, articles - 0

    擴展commons dbutils的JavaBean轉換方式

    Posted on 2011-04-26 16:41 寒武紀 閱讀(4266) 評論(0)  編輯  收藏 所屬分類: 數據庫 、Java

            引言:最近又用到dbutils,之前一直用Map映射的方式取出select的結果再手工做轉換。有寫過一篇文章說MapHandler方式的一個缺陷:關于commons dbutils組件的一個小缺陷分析 ,用這種方式,在項目不大的情況下,寫一些Map到JavaBean的轉換代碼工作量不大,但是在數據庫表過多并且表中的字段過多的情況下,這種重復的setter感覺有點煩。于是又重新思考了BeanHandler和BeanListHandler的情況,dbutils底層映射用的反射,性能上肯定有損失,不過在大多數項目規模不是很大的情況下,這點損失可以忽略,帶來的代碼減少卻是比較可觀。
            問題在哪里?先看一段官方的示例代碼:

    QueryRunner run = new QueryRunner(dataSource);

    // Use the BeanHandler implementation to convert the first
    // ResultSet row into a Person JavaBean.
    ResultSetHandler<Person> h = new BeanHandler<Person>(Person.class);

    // Execute the SQL statement with one replacement parameter and
    // return the results in a new Person object generated by the BeanHandler.
    Person p = run.query(
        
    "SELECT * FROM Person WHERE name=?", h, "John Doe");

            這里有個地方有約束,就是要求示例中的JavaBean類Person中的字段定義要和數據庫的字段定義一致。Java的命名習慣一般是駱峰寫法,例如userId,那么數據庫中就必須定義為userId,而問題在于:有時候我們需要數據庫中字段的定義格式與JavaBean的命名不一樣,比如數據庫定義為:user_id,而JavaBean則定義為userId
            看源代碼可能有點費時間,在官方的example頁面的最下面果然有一段關于自定義BeanProcessor的指引。摘錄出來:

          BasicRowProcessor uses a BeanProcessor to convert ResultSet columns into JavaBean properties. You can subclass and override processing steps to handle datatype mapping specific to your application. The provided implementation delegates datatype conversion to the JDBC driver.
          BeanProcessor maps columns to bean properties as documented in the BeanProcessor.toBean() javadoc. Column names must match the bean's property names case insensitively. For example, the firstname column would be stored in the bean by calling its setFirstName() method. However, many database column names include characters that either can't be used or are not typically used in Java method names. You can do one of the following to map these columns to bean properties:
          1. Alias the column names in the SQL so they match the Java names: select social_sec# as socialSecurityNumber from person
          2. Subclass BeanProcessor and override the mapColumnsToProperties() method to strip out the offending characters.


          大概意思就是提供二種方式:一種就是最直接的,用as關鍵字把colName重命名,另一種方式就是繼承BeanProcessor類,重寫mapColumnsToProperties()方法。
          那當然是第二種方式更加具有代表性。嘗試了一下。代碼如下:
        
     1public class CustomBeanProcessor extends BeanProcessor {
     2    
     3    @Override
     4    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
     5            PropertyDescriptor[] props) throws SQLException {
     6        int cols = rsmd.getColumnCount();
     7        int columnToProperty[] = new int[cols + 1];
     8        Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
     9
    10        for (int col = 1; col <= cols; col++{
    11            String columnName = rsmd.getColumnLabel(col); 
    12            if (null == columnName || 0 == columnName.length()) {
    13              columnName = rsmd.getColumnName(col);
    14            }

    15            columnName = colNameConvent(columnName); // 在這里進行數據庫表columnName的特殊處理
    16            for (int i = 0; i < props.length; i++{
    17
    18                if (columnName.equalsIgnoreCase(props[i].getName())) {
    19                    columnToProperty[col] = i;
    20                    break;
    21                }

    22            }

    23        }

    24        return columnToProperty;
    25    }

    26
    27    /**
    28     * 數據庫列名重新約定
    29     * @param columnName
    30     * @return
    31     */

    32    private String colNameConvent(String columnName) {
    33        String[] strs = columnName.split("_");
    34        String conventName = "";
    35        for (int i = 0; i < strs.length; i++{
    36            conventName += StringUtils.capitalize(strs[i]);
    37        }

    38        StringUtils.uncapitalize(conventName);
    39        return conventName;
    40    }

    41}

            注意mapColumnsToProperties方法的邏輯是從父類的方法中直接復制出來的,然后在第15行那里變了個戲法,這里的columnName就是從數據庫中讀出來的,自定義一個private方法用于轉換命名,這里你就可以添加自己的命名約束。例如上面就是把 user_id 轉化為Java的駱峰寫法:userId
           再深入一層思考,你可以在這里進行更多擴展,以便讓自己可以選擇不同的命名轉換方式。定義了這個Processor之后,下面看看如何調用:
    Connection conn = ConnectionManager.getInstance().getConnection();
    QueryRunner qr 
    = new QueryRunner();
    CustomBeanProcessor convert 
    = new CustomBeanProcessor();
    RowProcessor rp 
    = new BasicRowProcessor(convert);
    BeanHandler
    <User> bh = new BeanHandler<User>(User.class, rp);
    User u 
    = qr.query(conn, sql, bh, params);
    DbUtils.close(conn);
          是不是非常靈活?如果是想返回List結果的,就把BeanHandler替換成BeanListHander類,還可以再進一步封裝這些操作,抽象到公共模塊中去,讓外部直接傳入sql語句和Class就能直接返回想要的結果,當然你得增加泛型的定義。同樣舉一個封裝的例子:
     1protected <T> List<T> selectBeanList(Connection conn, String sql, Class<T> type,
     2            Object[] params) throws Exception {
     3        log.debug("select sql:[" + sql + "]");
     4        QueryRunner qr = new QueryRunner();
     5        CustomBeanProcessor convert = new CustomBeanProcessor();
     6        RowProcessor rp = new BasicRowProcessor(convert);
     7        ResultSetHandler<List<T>> bh = new BeanListHandler<T>(type, rp);
     8        List<T> list = qr.query(conn, sql, bh, params);
     9        return list;
    10    }

            至于為什么擴展這個方法就可以實現這個邏輯就得去跟源代碼看它的內部實現,用了一些JavaBean的處理和反映的技巧來做的。具體就不說。
            總結:commons組件都設計得非常好,可擴展性和實用性都非常高。雖然上面舉例實現了轉換邏輯的替換,但是仍然需要開發人員在設計數據庫的時候和寫JavaBean時都要嚴格做好規范,避免產生不必要的問題。這方面Ruby On Rails就直接內部實現,動態語言的優點特別能體現,同時強制你在設計時必須用這種方式,典型的約定優于配置原則。當然,在dbutils里你愿意二種字段名都一樣也無可厚非。
           缺點:BeanProcessor是不支持關聯查詢的,所以上面的方式也只能局限于單表的轉換,這點就不如myBatis和Hibernate,當然用這二個就引入了一些復雜性,如何權衡需要自己衡量,哪個用得好都一樣。本人就不喜歡myBatis那種把SQL寫到XML中的方式,見過太復雜的SQL最終在XML里面變得面目全非,如果是接手別人的代碼,是很痛苦的,而且你無法避免只修改XML而不改Java,既然二者都要改,那直接寫Java里又有什么區別?簡單就是美。格式和注釋寫好一點同樣很容易理解!

    剛進場的時候戲就落幕
    主站蜘蛛池模板: 久久免费国产精品| www成人免费视频| av无码国产在线看免费网站 | 日韩亚洲Av人人夜夜澡人人爽 | 中文字幕a∨在线乱码免费看| 亚洲AⅤ视频一区二区三区| 国产亚洲福利精品一区二区 | 久久久久亚洲AV成人网人人网站| 黄床大片30分钟免费看| 一本久到久久亚洲综合| 精品97国产免费人成视频| 亚洲日韩v无码中文字幕 | 日本牲交大片免费观看| 亚洲AⅤ男人的天堂在线观看 | 最近免费最新高清中文字幕韩国| 久久亚洲精品无码AV红樱桃| 亚洲美女免费视频| 国产成人精品日本亚洲语音 | 亚洲成a人片在线不卡一二三区 | 99久久精品免费视频| 亚洲国产精品综合福利专区| 毛色毛片免费观看| 瑟瑟网站免费网站入口| 国产成人亚洲综合无码| 香港a毛片免费观看| 亚洲中文无码永久免费| 免费观看在线禁片| 日木av无码专区亚洲av毛片| 91情侣在线精品国产免费| 亚洲第一街区偷拍街拍| 国产亚洲综合成人91精品| 最近中文字幕高清免费中文字幕mv | 国产小视频在线免费| 无码天堂亚洲国产AV| 久久亚洲国产成人亚| 久久WWW色情成人免费观看| 人妻巨大乳hd免费看| 亚洲成熟xxxxx电影| 国产高清免费的视频| 二区久久国产乱子伦免费精品| 亚洲理论精品午夜电影|