眾裏尋他千百度-- 輕量級持久化框架
-
- 初衷
- 純JDBC
- 持久化框架
- 輕量級持久化框架
- 設計思路
- 怎麽使用
- 依賴
- 數據庫配置
- 正式使用
- 數據庫表結構
- Java Bean結構
- 從數據庫獲取一條記錄並轉為對象
- 高級版
- 數據庫內記錄
- JavaBean結構
- 拓展
- 總結
- 初衷
起了一個比較文藝的標題,但是仍然感覺不能 表達出接下來這個工具的文雅。 雖然這個庫是前幾個月寫的了,但是經過了近期小項目的考驗,愈發覺得這款輕量級的庫應該被更多的開發者所知曉,於是“臭不要臉”地寫了這篇介紹性的文章。
最新版本:衣帶漸寬終不悔,為伊消得人憔悴–DbHelper增強版
初衷
對於如日中天的編程語言老大哥Java而言,其擁躉者數目定然不少。
純JDBC
基本上而言,到達一定的階段之後,就一定會接觸到數據庫了。一開始寫純正的JDBC的時候,不知道別人是怎麽想的,但是對我個人而言,簡直是 如坐針氈。
先不論使用Statement還是使用PreparedStatement來拼湊SQL語句,單單是處理ResultSet就是個不折不扣的煩心事。
持久化框架
其實這並不是特例,大部分人都會有這麽個感受。不然Hibernate等這些持久化框架也不會誕生了不是。但這也帶來了一些弊端。
-
首先對於新手而言,使用這些框架需要一定的學習成本,學習曲線不夠平緩,這就有可能磨滅其學習的興趣。
-
其次,對於小項目而言,動用重量級的框架,就顯得“大材小用”了。雖然這並不是說部可以,卻顯得有點不合時宜。
輕量級持久化框架
相信大家都了解Apache的dbutils吧。大大地簡化了JDBC的處理,封裝了很多必須的操作。但是我個人認為它並不能稱之為一個框架。 充其量是一個JDBC的Wrap版,也就是對JDBC進行了簡單的封裝的一個工具。
於是,在研究了其工作原理之後,結合Apache的另一個BeanUtils庫,我制作了一個比較輕量級的數據庫持久化框架。(姑且稱之為框架吧,(^__^) 嘻嘻……)
設計思路
以我個人,不長的開發經驗來看。“過猶不及,過滿則虧”。所以對於框架的設計原則也應該如此。
不能什麽都做,要適時的將權限放開,增加拓展。
仔細思考了,到底要拿它來做什麽? 如何添加拓展?這些問題之後,我就開始著手編碼了。
期間運用了 蹩腳的泛型和反射操作,自動化的解剖Bean,來解決與數據庫之間的持久化和反序列化處理等等。
大體的“架構” 可用下圖表示:
怎麽使用?
介紹到如何使用,我覺得還是以實際的使用案例來介紹比較好,這樣更有說服力。
依賴
因為這個框架依賴於apache的一些jar,所以我們需要將下列依賴添加到自己的項目中。(我個人建議在項目中新建一個lib包來放置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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
基本上來說,大家都會有專門的BaseDAO類,那麽在這個類裏面進行數據庫配置的註冊事件是最合適不過了。
import dbhelper.DbHelper;
/**
* @author 郭 璞
*
*/
public class BaseDAO {
/**
* 使用靜態代碼塊的方式,在程序的DAO層運行之前就註冊好 DbHelper ,做好對數據源的註冊
*/
static {
try {
DbHelper.register();
} catch (Exception e) {
throw new RuntimeException(e + "\n 數據源未註冊成功");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
這樣,其他的DAO只需要繼承這個類就可以了,我們就不用關心數據源的問題了,底層框架會自動的幫我們搞定。
正式使用
有一點需要強調的是,數據庫中的字段要和Java Bean中的屬性名保持高度的一致,否則框架不能正確的找到其隸屬的字段信息。畢竟框架不是萬能的嘛。
數據庫表結構
Java Bean結構
如圖所示, 按照這樣來設計就可以了。
從數據庫獲取一條記錄,並轉為對象
/**
* 根據用戶名查找其個人的詳細的信息
*
* @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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
與此同時,數據庫中的記錄如下圖:
下面使用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;
}
··· ···
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
然後就是 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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
然後再來看看JUnit的測試結果:
果不其然,還是獲取到了正確的數據。這一點可以和數據庫中原始的信息進行對比。
拓展
為了使得這個框架更加的靈活, 滿足大部分的定制性的需求,這裏給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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
使用的時候只需要將組裝好的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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
如此,基本上可以滿足JDBC編程時遇到的情況了。
總結
其實博主本人真的是一個愛分享,熱心腸的有誌青年。今天寫這篇文章的一個很重要的原因就是想幫助那些飽受JDBC之苦的開發人員,今早的以一種優雅的方式脫離苦海。
這篇文章從應用性的角度而言,應該算是比較詳細的了。但是基本上沒有討論底層的實現。
如果您對這個輕量級的框架感興趣的話,不妨點點左側的友情鏈接。
- 想要源碼: 點擊藍色的“GitHub”
- 一起討論: 點擊紅色的“點我聊天”(我有可能會不在線)
- 聯系方式: 發私信或者從GitHub上找到我的郵箱。
最後,衷心希望需要的人 能從中獲得幫助。
再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://blog.csdn.net/jiangjunshow
眾裏尋他千百度-- 輕量級持久化框架