JDBC自定義套件封裝

一浙滤、背景

Java開發(fā)人員現(xiàn)在對數(shù)據(jù)庫的操作一般會用到諸如像Hibernate,Mybatis,SpringJdbcTemplate等ORM組件,但是這些組件是怎樣從原始的編碼方式一步步封裝過來的呢?

二、最原始的編碼方式

如下面代碼所示:在筆者05年剛畢業(yè)的時候,曾經(jīng)是這樣寫Jdbc訪問數(shù)據(jù)庫的.

/**

*

*類描述:用來測試jdbc連接的類(oracle,mysql)

*創(chuàng)建人:keven

*@versionV1.0

*/

public?class?DBConnectionTest{

/**

*一個數(shù)據(jù)庫的連接我們要具備三項條件楞慈,一個是數(shù)據(jù)庫地址,

*一個是訪問數(shù)據(jù)庫的用戶名和密碼

*一個是數(shù)據(jù)庫的類型

*@Title: testDBConnection

*@Description:TODO

*/

public?static?void?testDBConnection(){

Connection con =?null;//一個數(shù)據(jù)庫連接

PreparedStatement pre =?null;//預(yù)編譯對象

ResultSet result =?null;//結(jié)果集

try{

//加載MySQL的驅(qū)動程序

Class.forName("com.mysql.jdbc.Driver");

//連接數(shù)據(jù)庫的路徑(關(guān)鍵字+IP地址+數(shù)據(jù)庫名稱)

String url="jdbc:mysql://127.0.0.1/open";

//數(shù)據(jù)庫的用戶名

String user="root";

//數(shù)據(jù)庫密碼

String password="123456";

System.out.println("現(xiàn)在開始連接MYSQL數(shù)據(jù)庫");

//獲取連接

con= DriverManager.getConnection(url,user,password);

System.out.println("連接MYSQL數(shù)據(jù)庫成功");

String sql="SELECT * FROM USER";

//實例化預(yù)編譯語句對象

pre=con.prepareStatement(sql);

//pre.setInt(1, deptId);

//返回結(jié)果集

result=pre.executeQuery();

//將結(jié)果集放到我們的java對象

while(result.next()){

System.out.println(result.getObject(1)+"----"+result.getObject(2)

+"----"+result.getObject(3)+"----"+result.getObject(4)+"----");

}

}catch(Exceptione) {

e.printStackTrace();

}finally{

try{

if(result!=null){

result.close();

}

if(pre!=null){

pre.close();

}

if(con!=null){

con.close();

}

}catch(Exceptione) {

e.printStackTrace();

}

}

}

public?static?void main(String[]args) {

DBConnectionTest.testDBConnection();

}

}

三、封裝的過程和思路

總得來說,Java組件封裝的原則就是高內(nèi)聚,低耦合,直白一點的解釋就是將重復性的代碼提取出去作為工具類,盡量減少類與類之間的固定依賴.

1)DbUtil工具類

通過查看最原始編碼方式的代碼,我們可以看出,獲取數(shù)據(jù)庫的連接和關(guān)閉數(shù)據(jù)庫連接的代碼,在每一次操作中都需要,所以我們可以思考一下,將這部分代碼提取出去.

*新建DbUtil工具類,用于數(shù)據(jù)庫的開連接和關(guān)連接

/**

*類描述:封裝第一步 我們把取得連接和關(guān)閉連接抽取出來成為獨立的方法放入工具類里面

*第二步:我們是否要考慮將我們的數(shù)據(jù)庫連接的四要素(類型氮唯,用戶名 密碼 褒侧,地址 抽取出來做成配置文件的方式)

*創(chuàng)建人:keven

*@versionV1.0

*/

public?class?DbUtil {

public?static?Connection getConnection()?throws?Exception {

Connection con;

//加載MySQL的驅(qū)動程序

Class.forName("com.mysql.jdbc.Driver");

//連接數(shù)據(jù)庫的路徑(關(guān)鍵字+IP地址+數(shù)據(jù)庫名稱)

String url="jdbc:mysql://127.0.0.1/open";

//數(shù)據(jù)庫的用戶名

String user="root";

//數(shù)據(jù)庫密碼

String password="123456";

System.out.println("現(xiàn)在開始連接MYSQL數(shù)據(jù)庫");

//獲取連接

con= DriverManager.getConnection(url,user,password);

return?con;

}

/**

*關(guān)閉數(shù)據(jù)庫連接

*@Title: closeConnection

*@Description:TODO

*@paramcon

*@parampre

*@paramresult

*/

public?static?void?closeConnection(Connection con, PreparedStatement pre,

ResultSet result) {

try{

if(result!=null){

result.close();

}

if(pre!=null){

pre.close();

}

if(con!=null){

con.close();

}

}catch(Exceptione) {

//TODOAuto-generated catch block

e.printStackTrace();

}

}

}

通過工具類的封裝,我們可以繼續(xù)在工具類里面將數(shù)據(jù)庫的信息通過配置文件加載,以及啟用流行的連接池技術(shù),在這里不在贅述.

2)增刪改方法的封裝

在封裝了DbUtil工具類的基礎(chǔ)上,我們試著做一個單表的增刪改查,請看以下代碼:

/**

*關(guān)于用戶表的增刪改查方法

*@authorkeven

*/

public?class?UserDaoTest{

/**

* user表的增加方法

*/

public?int?addUser(User user){

int?rows= 0;

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

try{

con= DbUtil.getConnection();

String addSql=" INSERT INTO

USER(NAME,AGE,SEX)VALUES(?,?,?)";

pre=con.prepareStatement(addSql);

pre.setString(1,user.getName());

pre.setInt(2,user.getAge());

pre.setString(3,user.getSex());

rows=pre.executeUpdate();

}catch(Exceptione){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con,pre,result);

}

return?rows;

}

/**

* user表的刪除方法

*/

public?int?delUser(int?userId){

int?rows= 0;

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

try{

con= DbUtil.getConnection();

String deleteSql="DELETE FROM USER WHERE ID = ?";

pre=con.prepareStatement(deleteSql);

pre.setInt(1,userId);

rows=pre.executeUpdate();

}catch(Exceptione){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con,pre,result);

}

returnrows;

}

/**

* user表的更新方法

*/

public?int?updateEmp(Useruser){

int?rows= 0;

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

try{

con= DbUtil.getConnection();

String updateSql=" UPDATE USER SET NAME =?, AGE=?,SEX=? WHERE ID =?

";

pre=con.prepareStatement(updateSql);

pre.setString(1,user.getName());

pre.setInt(2,user.getAge());

pre.setString(3,user.getSex());

pre.setInt(4,user.getId());

rows=pre.executeUpdate();

}catch(Exceptione){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con,pre,result);

}

return?rows;

}

/**

* user表的查詢方法

*/

public?List?getUserList(){

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

List?userList=new?ArrayList();

try{

con= DbUtil.getConnection();

String sql="SELECT

ID,NAME,AGE,SEX FROM USER ";

//實例化預(yù)編譯語句對象

pre=con.prepareStatement(sql);

//pre.setInt(1, deptNo);

//返回結(jié)果集

result=pre.executeQuery();

//將結(jié)果集放到我們的java對象

User user;

while(result.next()){

user=new?User();

user.setId(result.getInt("id"));

user.setName(result.getString("name"));

user.setAge(result.getInt("age"));

user.setSex(result.getString("sex"));

userList.add(user);

}

}catch(Exceptione) {

//TODOAuto-generated catch block

e.printStackTrace();

}finally{

//逐一將上面的對象全部關(guān)閉良风,因為如果不關(guān)閉的話會影響數(shù)據(jù)庫的性能,并且占用資源

//逐一關(guān)閉的順序 最后使用的最先關(guān)閉

DbUtil.closeConnection(con,pre,result);

}

return?userList;

}

}

進一步觀察增刪改方法,除了Sql語句和參數(shù)傳入的不同,其他代碼其實也是重復的,我們是否可以考慮將這些公用的代碼也提取出去呢?

偉大的Java程序員們都是”懶鬼”,一切都是為了少些一些重復的代碼以提高工作效率.

我們可以新建一個模板類JdbcTemplate,對增刪改方法進行封裝,外部只需要傳入sql語句和sql語句需要用到的參數(shù).

/**

*類描述:jdbcdao模板類

*通過參數(shù)的動態(tài)傳入來簡化dao類的代碼

* 1:sql

* 2:sql需要傳入的變量的值

*創(chuàng)建人:keven

*/

public?class?JdbcTemplate{

/**

*封裝增刪改的模板方法

*@Title: updateTemplate

*@Description:TODO

*@paramsql

*@paramparams

*@return

*/

public?int?updateTemplate(String sql,Object[] params){

int?rows= 0;

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

try{

con= DbUtil.getConnection();

pre=con.prepareStatement(sql);

//設(shè)置sql所需要的參數(shù)

if(params!=null){

for(int?i=0;i

pre.setObject(i+1,params[i]);

}

}

rows=pre.executeUpdate();

}catch(Exceptione){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con,pre,result);

}

return?rows;

}

}

通過上面步驟的封裝,我們再來看看讓對單表增刪改的操作是如何方便簡單的,新建一個單表增刪改的測試類,繼承我們封裝的模板類,代碼如下:

/**

*繼承了模板類的單表增刪改操作

*@authorkeven

*

*/

public?class?UserDaoTemplateTest?extends?JdbcTemplate {

/**

* user表的增加方法

*@Title:adduser

*@Description:TODO

*@paramuser

*@return

*/

public?int?addUser(User user){

String addSql=" INSERT INTO

USER(NAME,AGE,SEX)VALUES(?,?,?)";

Object[]params=newObject[]{user.getName(),user.getAge(),user.getSex()};

return?this.updateTemplate(addSql,params);

}

/**

* user表的刪除方法

*@Title:deluser

*@Description:TODO

*@paramuserNo

*@return

*/

public?int?deluser(int?userId){

String deleteSql="DELETE FROM USER WHERE ID=?";

Object[]params=newObject[]{userId};

return?this.updateTemplate(deleteSql,params);

}

/**

* user表的修改方法

*@Title:updateuser

*@Description:TODO

*@paramuser

*@return

*/

public?int?updateUser(Useruser){

String updateSql=" UPDATE USER SET NAME =?, AGE=?,SEX=? WHERE ID =?

";

Object[]params=newObject[]{user.getName(),user.getAge(),user.getSex(),user.getId()};

return?this.updateTemplate(updateSql,params);

}

}

回過頭看看我們的封裝過程和代碼,是不是對于開發(fā)人員來講,越來越簡單,代碼寫的越來越少,這就是Java在實際開發(fā)過程中需要用到大量前輩們封裝的組件的原因.

3)查詢方法的封裝

在增刪改方法的封裝過程當中,我們發(fā)現(xiàn),增刪改的操作,方法的返回值是固定的,但是查詢方法的返回值是不固定的,查詢不同的表,返回的是不同對象,也有可能是返回的其他類型的值.

通過以上分析,我們封裝查詢方法的時候,只能返回一個固定格式的對象或者列表,讓執(zhí)行查詢的人來解析固定格式的結(jié)果得到自己想要的返回值.

兩種方式:

a:返回一個List結(jié)構(gòu)

在JdbcTemplate模板類面新加查詢模板方法

/**

*利用Map結(jié)構(gòu)來獲取每行的記錄 以List返回

*@Title:queryForList

*@Description:TODO

*@paramsql

*@paramparams

*@return

*/

public?List??queryForList(String sql,Object[]params){

List?mapList=new?ArrayList();

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

try{

con= DbUtil.getConnection();

pre=con.prepareStatement(sql);

//設(shè)置sql所需要的參數(shù)

if(params!=null){

for(inti=0;i

pre.setObject(i+1,params[i]);

}

}

result=pre.executeQuery();

//怎么講result的結(jié)果集返回給mapList

//1:得到結(jié)果集里面每一個元數(shù)據(jù)的對象

ResultSetMetaData metaData=result.getMetaData();

//獲取結(jié)果集的列數(shù)

int?columnNum=metaData.getColumnCount();

Map?mapObj;

while(result.next()){

mapObj=newHashMap();

for(int?i= 0;i<columnName;i++){

//根據(jù)索引獲取列名(map的key)

String columnName=metaData.getColumnLabel(i+1);

//根據(jù)列名或者值(map的value)

Object value=result.getObject(columnName);

mapObj.put(columnName,value);

}

mapList.add(mapObj);

}

}catch(Exceptione){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con,pre,result);

}

return?mapList;

}

這種封裝方式在執(zhí)行查詢時候,獲取的結(jié)果是List>結(jié)構(gòu)的值,需要自己再進行轉(zhuǎn)化闷供,但是對于查詢來說烟央,就非常的簡單了。

在UserDaoTemplateTest類里面新加查詢方法

//查詢方法如果要封裝成模板類

//主要要去思考每一個查詢的方法返回的類型是不一樣的

//1:List>

//2:我們需要再Dao層就做到返回我們需要的List

public?List??getUserListMap(){

String sql="SELECT ID,NAME,AGE,SEX FROM USER ";

Object[]params=new?Object[]{};

return?this.queryForList(sql,params);

}

B:返回一個接口的匿名內(nèi)部類

這種方式歪脏,封裝起來稍微復雜一些疑俭,但是對于查詢方來說,就可以直接在查詢方法里面獲取自己想要的對象婿失,返回List,

非常簡單钞艇。

步驟:

*新建一個接口RowMapper,成員是一個匿名的內(nèi)部類

/**

*通過傳入ResultSet對象講每一條記錄通過泛型映射成對應(yīng)的對象 使用的是接口的匿名內(nèi)部類

*/

public?interface?RowMapper{

publicT mappingRow(ResultSet rs,int?rownum)throwsSQLException;

}

*在JdbcTemplate模板類里面新增模板查詢方法

public List queryForList(RowMappermapper,Stringsql,Object[]params){

List?returnResult=new?ArrayList();

Connection con=null;//一個數(shù)據(jù)庫連接

PreparedStatement pre=null;//預(yù)編譯對象

ResultSet result=null;//結(jié)果集

try{

con= DbUtil.getConnection();

pre=con.prepareStatement(sql);

//設(shè)置sql所需要的參數(shù)

if(params!=null){

for(int?i=0;i

pre.setObject(i+1,params[i]);

}

}

result=pre.executeQuery();

int ?rownum= 0;

while(result.next()){

rownum++;

returnResult.add(mapper.mappingRow(result,rownum));

}

}catch(Exceptione){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con,pre,result);

}

return?returnResult;

}

*查詢的時候豪硅,通過實現(xiàn)匿名的內(nèi)部類來獲取結(jié)果哩照,直接映射到Java對象當中 ,如代碼所示懒浮,在UserDaoTemplateTest中進行查詢

/**

*利用接口里面匿名內(nèi)部類的實現(xiàn)來講結(jié)果集賦給對象的值移到dao層實現(xiàn)

*@Title:getuserList

*@Description:TODO

*@paramdeptNo

*@return

*/

public?List?getUserList(){

List userList=new?ArrayList();

Stringsql="SELECT ID,NAME,AGE,SEX FROM USER ";

Object[]params=newObject[]{};

userList=?this.queryForList(newRowMapper(){

public?User mappingRow(ResultSet rs,int?rownum)throwsSQLException {

Useruser=newUser();

user.setId(rs.getInt("id"));

user.setName(rs.getString("name"));

user.setAge(rs.getInt("age"));

user.setSex(rs.getString("sex"));

return?user;

}

},sql,params);

return?userList;

}

最終的代碼目錄結(jié)構(gòu)如下飘弧,希望對大家的學習有所幫助。


四砚著、總結(jié)

通過以上的封裝過程次伶,我們可以了解到Java封裝組件的一個基本思路,有助于大家以后在用到相關(guān)的ORM組件時稽穆,對它們有一個更深得到認識冠王,當然,本篇文章封裝的代碼只是冰山一角舌镶,如果需要達到像Mybatis和Hibernate等組件的高度柱彻,還有很長的一段的路要走,有興趣的同學可以查看一下SpringJdbcTemplate的源碼乎折,其中的思想是跟它不謀而合的绒疗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骂澄,隨后出現(xiàn)的幾起案子吓蘑,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磨镶,死亡現(xiàn)場離奇詭異溃蔫,居然都是意外死亡,警方通過查閱死者的電腦和手機琳猫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門伟叛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脐嫂,你說我怎么就攤上這事统刮。” “怎么了账千?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵侥蒙,是天一觀的道長。 經(jīng)常有香客問我匀奏,道長鞭衩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任娃善,我火速辦了婚禮论衍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘聚磺。我一直安慰自己坯台,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布瘫寝。 她就那樣靜靜地躺著捂人,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矢沿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天酸纲,我揣著相機與錄音捣鲸,去河邊找鬼。 笑死闽坡,一個胖子當著我的面吹牛栽惶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疾嗅,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼外厂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了代承?” 一聲冷哼從身側(cè)響起汁蝶,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后掖棉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體墓律,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年幔亥,在試婚紗的時候發(fā)現(xiàn)自己被綠了耻讽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡帕棉,死狀恐怖针肥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情香伴,我是刑警寧澤慰枕,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站瞒窒,受9級特大地震影響捺僻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜崇裁,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一匕坯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拔稳,春花似錦葛峻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至轻绞,卻和暖如春采记,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背政勃。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工唧龄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奸远。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓既棺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親懒叛。 傳聞我的和親對象是個殘疾皇子丸冕,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容