衆裏尋他千百度-- 輕量級持久化框架

起了一個比較文藝的標題,但是仍然感覺不能 表達出接下來這個工具的文雅。 雖然這個庫是前幾個月寫的了,但是經過了近期小項目的考驗,愈發覺得這款輕量級的庫應該被更多的開發者所知曉,於是“臭不要臉”地寫了這篇介紹性的文章。


最新版本:衣帶漸寬終不悔,爲伊消得人憔悴–DbHelper增強版

初衷

對於如日中天的編程語言老大哥Java而言,其擁躉者數目定然不少。

純JDBC

基本上而言,到達一定的階段之後,就一定會接觸到數據庫了。一開始寫純正的JDBC的時候,不知道別人是怎麼想的,但是對我個人而言,簡直是 如坐鍼氈。

先不論使用Statement還是使用PreparedStatement來拼湊SQL語句,單單是處理ResultSet就是個不折不扣的煩心事。

持久化框架

其實這並不是特例,大部分人都會有這麼個感受。不然Hibernate等這些持久化框架也不會誕生了不是。但這也帶來了一些弊端。

  • 首先對於新手而言,使用這些框架需要一定的學習成本,學習曲線不夠平緩,這就有可能磨滅其學習的興趣。

  • 其次,對於小項目而言,動用重量級的框架,就顯得“大材小用”了。雖然這並不是說部可以,卻顯得有點不合時宜。

輕量級持久化框架

相信大家都瞭解Apache的dbutils吧。大大地簡化了JDBC的處理,封裝了很多必須的操作。但是我個人認爲它並不能稱之爲一個框架。 充其量是一個JDBC的Wrap版,也就是對JDBC進行了簡單的封裝的一個工具。

於是,在研究了其工作原理之後,結合Apache的另一個BeanUtils庫,我製作了一個比較輕量級的數據庫持久化框架。(姑且稱之爲框架吧,(^__^) 嘻嘻……)

設計思路

以我個人,不長的開發經驗來看。“過猶不及,過滿則虧”。所以對於框架的設計原則也應該如此。

不能什麼都做,要適時的將權限放開,增加拓展。

仔細思考了,到底要拿它來做什麼? 如何添加拓展?這些問題之後,我就開始着手編碼了。

期間運用了 蹩腳的泛型和反射操作,自動化的解剖Bean,來解決與數據庫之間的持久化和反序列化處理等等。

大體的“架構” 可用下圖表示:

架構圖

怎麼使用?

介紹到如何使用,我覺得還是以實際的使用案例來介紹比較好,這樣更有說服力。

依賴

因爲這個框架依賴於apache的一些jar,所以我們需要將下列依賴添加到自己的項目中。(我個人建議在項目中新建一個lib包來放置jar文件)
jar依賴

數據庫配置

因爲JDBC是跨數據庫的,所以我們只要提供針對不同的數據庫廠商的jar,那麼這個框架也可以跨數據庫。

如上圖可以看到 src目錄下有一個db.cfg.xml的文件。這裏面就放置了之前要手動處理的數據庫配置信息了。比葫蘆畫瓢,按照下面的代碼配置自己的環境即可。

<?xml version="1.0" encoding="UTF-8" ?>
<project>
    <database name="mysql">
        <driver>com.mysql.jdbc.Driver</driver>
        <url>jdbc:mysql://localhost:3306/mydb</url>
        <user>root</user>
        <password>mysql</password>
    </database>

</project>

基本上來說,大家都會有專門的BaseDAO類,那麼在這個類裏面進行數據庫配置的註冊事件是最合適不過了。

import dbhelper.DbHelper;

/**
 * @author 郭 璞
 *
 */
public class BaseDAO {

    /**
     * 使用靜態代碼塊的方式,在程序的DAO層運行之前就註冊好 DbHelper ,做好對數據源的註冊
     */
    static {
        try {
            DbHelper.register();
        } catch (Exception e) {
            throw new RuntimeException(e + "\n 數據源未註冊成功");
        }
    }

}

這樣,其他的DAO只需要繼承這個類就可以了,我們就不用關心數據源的問題了,底層框架會自動的幫我們搞定。

正式使用

有一點需要強調的是,數據庫中的字段要和Java Bean中的屬性名保持高度的一致,否則框架不能正確的找到其隸屬的字段信息。畢竟框架不是萬能的嘛。

數據庫表結構

數據庫表字段

Java Bean結構

JavaBean字段

如圖所示, 按照這樣來設計就可以了。

從數據庫獲取一條記錄,並轉爲對象


    /**
     * 根據用戶名查找其個人的詳細的信息
     * 
     * @param user_name
     * @return
     */
    public User selectUser(String user_name) {
        // 聲明數據庫連接對象
        Connection conn = null;
        try {
            // 初始化 數據庫連接對象,避免出現空指針調用異常問題
            conn = DbHelper.getConn();
            // 組裝 SQL 查詢語句
            String sql = "select * from java_user where user_name= ?";
            // 實例化數據庫查詢對象
            QueryRunner queryRunner = new QueryRunner();
            // 採用 泛型編程技術 ,從底層開始直接獲取數據庫行記錄到對象的自動轉化流程
            User user = queryRunner.query(conn, sql, new BeanHandler<User>(User.class), user_name);
            // 釋放數據庫鏈接資源
            DbHelper.release(conn);
            return user != null ? user : null;
        } catch (Exception e) {
            throw new RuntimeException(" :\n" + e);
        }
    }

與此同時,數據庫中的記錄如下圖:
數據庫中記錄信息

下面使用JUnit來測試一下,
Junit測試結果

可見,確實可以自動的幫助我們反序列化數據庫中的數據。


高級版

上面的小案例簡單的測試了一下,單個Bean對象的獲取,那麼如果說ResultSet內包含多條記錄呢? 這時候你可能會想,要是能自動的轉換成List,然後List裏面包裹着這些反序列化好的數據對象,該多好。

確實是這樣的,DbHelper也做到了。(^__^) 嘻嘻……

數據庫內記錄

閒言少敘,咱們直接開始吧。

數據庫內字段信息

JavaBean結構

按照約定,java Bean 內字段保持和數據庫內表結構字段一致即可。

public class Site {
    private String site_name;
    private String site_username;
    private String site_password;
    private Integer java_user_id;
    private Integer java_tag_tag_id;

    public String getsite_name() {
        return site_name;
    }
    ··· ···

然後就是 DAO層內的業務代碼了,還是那簡單的幾步。需要注意的是,接口回調的時候new的不再是BeanHandler了,而是BeanListHandler。這和返回的結果集直接相關。

public List<Site> selectAllSites(String user_name) {
        Connection conn = null;
        try {
            // 實例化 數據庫連接對象
            conn = DbHelper.getConn();
            // 拼接sql語句
            String sql = "select * from java_site where java_site.java_user_id=(select java_user.id from java_user where java_user.user_name='"
                    + user_name + "')";
            // 實例化查詢器
            QueryRunner queryRunner = new QueryRunner();
            // 獲取接口回調處理後的結果
            List<Site> sites = queryRunner.query(conn, sql, new BeanListHandler<Site>(Site.class));
            // 釋放數據庫鏈接資源
            DbHelper.release(conn);

            return sites != null ? sites : null;
        } catch (Exception e) {
            throw new RuntimeException(" :\n" + e);
        }
    }

然後再來看看JUnit的測試結果:
JUnit List 測試結果

果不其然,還是獲取到了正確的數據。這一點可以和數據庫中原始的信息進行對比。

拓展

爲了使得這個框架更加的靈活, 滿足大部分的定製性的需求,這裏給QueryRunner 額外進行了拓展。可以靈活的處理自己的業務需求。

/**
     * 根據給定的參數實現向數據庫中給定SQL語句的update,delete,insert 操作
     * 
     * @param conn
     *            數據庫連接對象,用戶不必關心其釋放問題,這裏自動將其釋放
     * @param sql
     *            數據庫查詢語句
     * @param params
     *            對應於SQL語句佔位符的參數列表
     * @throws Exception
     */
    public void update(Connection conn, String sql, Object... params) throws Exception {
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < params.length; i++) {
            ps.setObject((i + 1), params[i]);
        }
        ps.executeUpdate();
        DbHelper.release(conn, ps);
    }

使用的時候只需要將組裝好的sql語句傳給query方法即可。如:

/**
     * 更新 網站對應的標籤信息
     * 
     * @param site_name
     *            網站名稱
     * @param new_java_tag_tag_id
     *            新的標籤信息
     * @return
     */
    public boolean updateSiteTagID(String site_name, Integer new_java_tag_tag_id) {
        Connection conn = null;
        try {
            conn = DbHelper.getConn();
            String sql = "update java_site set java_tag_tag_id=? where site_name=?";
            QueryRunner queryRunner = new QueryRunner();
            Object[] params = { new_java_tag_tag_id, site_name };
            // 在完成更新操作後,底層會自動的斷開與數據庫的鏈接
            queryRunner.update(conn, sql, params);
            return true;
        } catch (Exception e) {
            throw new RuntimeException(" :\n" + e);
        }

    }

如此,基本上可以滿足JDBC編程時遇到的情況了。

總結

其實博主本人真的是一個愛分享,熱心腸的有志青年。今天寫這篇文章的一個很重要的原因就是想幫助那些飽受JDBC之苦的開發人員,今早的以一種優雅的方式脫離苦海。

這篇文章從應用性的角度而言,應該算是比較詳細的了。但是基本上沒有討論底層的實現。

如果您對這個輕量級的框架感興趣的話,不妨點點左側的友情鏈接。

友情鏈接

  • 想要源碼: 點擊藍色的“GitHub”
  • 一起討論: 點擊紅色的“點我聊天”(我有可能會不在線)
  • 聯繫方式: 發私信或者從GitHub上找到我的郵箱。

最後,衷心希望需要的人 能從中獲得幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章