JDBC自定義套件封裝

一、背景

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

二、最原始的編碼方式

如下面代碼所示:在筆者05年剛畢業的時候,曾經是這樣寫Jdbc訪問數據庫的.

/**

*

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

*創建人:keven

*@versionV1.0

*/

public?class?DBConnectionTest{

/**

*一個數據庫的連接我們要具備三項條件,一個是數據庫地址,

*一個是訪問數據庫的用戶名和密碼

*一個是數據庫的類型

*@Title: testDBConnection

*@Description:TODO

*/

public?static?void?testDBConnection(){

Connection con =?null;//一個數據庫連接

PreparedStatement pre =?null;//預編譯對象

ResultSet result =?null;//結果集

try{

//加載MySQL的驅動程序

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

//連接數據庫的路徑(關鍵字+IP地址+數據庫名稱)

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

//數據庫的用戶名

String user="root";

//數據庫密碼

String password="123456";

System.out.println("現在開始連接MYSQL數據庫");

//獲取連接

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

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

String sql="SELECT * FROM USER";

//實例化預編譯語句對象

pre=con.prepareStatement(sql);

//pre.setInt(1, deptId);

//返回結果集

result=pre.executeQuery();

//將結果集放到我們的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組件封裝的原則就是高內聚,低耦合,直白一點的解釋就是將重復性的代碼提取出去作為工具類,盡量減少類與類之間的固定依賴.

1)DbUtil工具類

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

*新建DbUtil工具類,用于數據庫的開連接和關連接

/**

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

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

*創建人:keven

*@versionV1.0

*/

public?class?DbUtil {

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

Connection con;

//加載MySQL的驅動程序

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

//連接數據庫的路徑(關鍵字+IP地址+數據庫名稱)

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

//數據庫的用戶名

String user="root";

//數據庫密碼

String password="123456";

System.out.println("現在開始連接MYSQL數據庫");

//獲取連接

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

return?con;

}

/**

*關閉數據庫連接

*@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();

}

}

}

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

2)增刪改方法的封裝

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

/**

*關于用戶表的增刪改查方法

*@authorkeven

*/

public?class?UserDaoTest{

/**

* user表的增加方法

*/

public?int?addUser(User user){

int?rows= 0;

Connection con=null;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

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;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

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;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

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;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

List?userList=new?ArrayList();

try{

con= DbUtil.getConnection();

String sql="SELECT

ID,NAME,AGE,SEX FROM USER ";

//實例化預編譯語句對象

pre=con.prepareStatement(sql);

//pre.setInt(1, deptNo);

//返回結果集

result=pre.executeQuery();

//將結果集放到我們的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{

//逐一將上面的對象全部關閉,因為如果不關閉的話會影響數據庫的性能,并且占用資源

//逐一關閉的順序 最后使用的最先關閉

DbUtil.closeConnection(con,pre,result);

}

return?userList;

}

}

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

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

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

/**

*類描述:jdbcdao模板類

*通過參數的動態傳入來簡化dao類的代碼

* 1:sql

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

*創建人:keven

*/

public?class?JdbcTemplate{

/**

*封裝增刪改的模板方法

*@Title: updateTemplate

*@Description:TODO

*@paramsql

*@paramparams

*@return

*/

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

int?rows= 0;

Connection con=null;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

try{

con= DbUtil.getConnection();

pre=con.prepareStatement(sql);

//設置sql所需要的參數

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);

}

}

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

3)查詢方法的封裝

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

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

兩種方式:

a:返回一個List結構

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

/**

*利用Map結構來獲取每行的記錄 以List返回

*@Title:queryForList

*@Description:TODO

*@paramsql

*@paramparams

*@return

*/

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

List?mapList=new?ArrayList();

Connection con=null;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

try{

con= DbUtil.getConnection();

pre=con.prepareStatement(sql);

//設置sql所需要的參數

if(params!=null){

for(inti=0;i

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

}

}

result=pre.executeQuery();

//怎么講result的結果集返回給mapList

//1:得到結果集里面每一個元數據的對象

ResultSetMetaData metaData=result.getMetaData();

//獲取結果集的列數

int?columnNum=metaData.getColumnCount();

Map?mapObj;

while(result.next()){

mapObj=newHashMap();

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

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

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

//根據列名或者值(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;

}

這種封裝方式在執行查詢時候,獲取的結果是List>結構的值,需要自己再進行轉化,但是對于查詢來說,就非常的簡單了。

在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:返回一個接口的匿名內部類

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

非常簡單。

步驟:

*新建一個接口RowMapper,成員是一個匿名的內部類

/**

*通過傳入ResultSet對象講每一條記錄通過泛型映射成對應的對象 使用的是接口的匿名內部類

*/

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;//一個數據庫連接

PreparedStatement pre=null;//預編譯對象

ResultSet result=null;//結果集

try{

con= DbUtil.getConnection();

pre=con.prepareStatement(sql);

//設置sql所需要的參數

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;

}

*查詢的時候,通過實現匿名的內部類來獲取結果,直接映射到Java對象當中 ,如代碼所示,在UserDaoTemplateTest中進行查詢

/**

*利用接口里面匿名內部類的實現來講結果集賦給對象的值移到dao層實現

*@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;

}

最終的代碼目錄結構如下,希望對大家的學習有所幫助。


四、總結

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容