您好,登錄后才能下訂單哦!
這篇文章主要講解了“JDBC DAO的設計理念是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JDBC DAO的設計理念是什么”吧!
JDBC DAO中Connection的含義
Connection表示了一個和數據庫的鏈接,底層需要有操作系統的Socket支持,所以Connection是一種資源,既然是一種資源,就需要按照建立,打開,使用,關閉的順序合理的使用。
Connection是Java數據庫操作的基礎,是進行一系列操作的基礎,所有的派生的操作,例如Statement,PreparedStatement,ResultSet等都由Connection直接或者間接的衍生。
如何獲得Connection呢?
方法一,使用DriverManager類來獲取,前提條件是數據庫驅動程序需要在classpath下(即使用數據庫鏈接的程序按照Java的方式可以訪問到)。
Connectionconn=DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.1:1521:ORCL",user,pwd);
方法二,使用數據庫連接池來獲取
什么是數據庫連接池呢,數據庫連接池是標準JavaEE容器的一種服務,例如Webspher,Weblogic,Tomcat等,容器預先建立一些數據庫鏈接,以便應用程序使用的時候從中借取,注意有借有還,當應用程序使用完了之后會將數據庫鏈接還回連接池。(數據源配置請參考其他文檔)
使用連接池的好處是,可以預先建立鏈接,減小在數據庫獲取上的相對時間。
使用連接池獲取數據庫鏈接的方式為:
InitialContextctx=newInitialContext(); DataSourceds=(DataSource)ctx.lookup("java:comp/env/jdbc/DataSource"); Connectionconn=ds.getConnection();
由于在配置數據庫連接池的時候已經定義了URL,用戶名,密碼等信息,所以在程序中使用的時候不需要傳入這些信息。
JDBC DAO中ConnectionManager定義
Connection用來專門管理數據庫鏈接,通常情況下ConnectionManager只有一個方法,調用這個方法將返回一個Connection的實例。通過ConnectionManager可以封裝Connection的獲取方式(例如開發的時候使用DriverManager,運用的時候使用DataSource的方式,但是不需要修改ConnectionManager之外的其他代碼)和追加Connection獲取之前之后的操作(例如針對Connection的屬性的設置)。
下面的代碼是一個ConnectionManager的代碼示例:
packagecom.jpleasure.jdbc.dao; importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.SQLException; publicclassConnectionManager{ publicstaticConnectiongetConnection()throwsDaoException{ Connectionconn=null; try{ conn=DriverManager.getConnection("","",""); }catch(SQLExceptione){ thrownewDaoException("cannotgetdatabaseconnection",e); } returnconn; } }
如果需要從開發模式變為運用模式,只需要將上述代碼修改為:
packagecom.jpleasure.jdbc.dao; importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.SQLException; publicclassConnectionManager{ publicstaticConnectiongetConnection()throwsDaoException{ Connectionconn=null; try{ Contextctx=newInitialContext(); DataSourceds=(DataSource)ctx.lookup("jdbc/dsname"); conn=ds.getConnection(); }catch(NamingExceptione){ thrownewDaoException("cannotfinddatasource",e); }catch(SQLExceptione){ thrownewDaoException("cannotgetdatabaseconnection",e); } returnconn; } } 如果需要預先設定Connection的一些屬性,也可以在上述代碼中設定,例如: packagecom.jpleasure.jdbc.dao; importjava.sql.Connection; importjava.sql.DriverManager; importjava.sql.SQLException; publicclassConnectionManager{ publicstaticConnectiongetConnection()throwsDaoException{ Connectionconn=null; try{ Contextctx=newInitialContext(); DataSourceds=(DataSource)ctx.lookup("jdbc/dsname"); conn=ds.getConnection(); conn.setAutoCommit(false); }catch(NamingExceptione){ thrownewDaoException("cannotfinddatasource",e); }catch(SQLExceptione){ thrownewDaoException("cannotgetdatabaseconnection",e); } returnconn; } } CommonDao定義 屬性和構造方法 通常情況下,CommonDao要有一個Connection的引用。所有一個CommonDao的實例的所有方法的調用都需要依賴于這個Connection。需要一個Connection的另外一個原因是如果各個方法需要保證在一個事務環境中(上下文中),必須保證所有的操作都在一個Connection上。 構造方法通常需要將類型為Connection的屬性實例化,例如: packagecom.jpleasure.jdbc.dao; importjava.sql.Connection; publicclassCommonDao{ privateConnectionconn; publicCommonDao()throwsDaoException{ this.conn=ConnectionManager.getConnection(); } }
事務方法
begin()
開始一個事務,調用CommonDao的begin方法之后,所以的后續操作將會在一個事務環境內,要么全部提交,要么全部回滾。
commit()
提交一個事務,必須在begin調用之后調用。且和rollback方法互斥。
rollback()
回滾一個事務,必須在begin方法調用之后調用。且和commit方法互斥。
事務的實現有兩種方法,一種是使用基于單一Connection的事務,另外一種方法是使用容器的JTA(JavaTransactionAPI)。需要注意的是***種方法可以在任何環境下使用,但是只能是針對單一的數據庫鏈接。第二種方法智能在支持JTA的JavaEE容器中使用(例如Websphere,Weblogic等,Tomcat默認不支持),但是支持多個Connection實例。
***種方法代碼為:
packagecom.jpleasure.jdbc.dao; importjava.sql.Connection; importjava.sql.SQLException; publicclassCommonDao{ privateConnectionconn; publicCommonDao()throwsDaoException{ this.conn=ConnectionManager.getConnection(); } publicvoidbegin()throwsDaoException{ if(conn!=null){ try{ conn.setAutoCommit(false); }catch(SQLExceptione){ thrownewDaoException("cannotbegintransaction",e); } }else{ thrownewDaoException("connectionnotopened!"); } } publicvoidcommit()throwsDaoException{ try{ if(conn!=null&&!conn.getAutoCommit()){ conn.commit(); conn.setAutoCommit(true); }else{ if(conn==null){ thrownewDaoException("connectionnotopened!"); }else{ thrownewDaoException("firstbeginthencommitplease!"); } } }catch(SQLExceptione){ thrownewDaoException("cannotcommittransaction!",e); } } publicvoidrollback()throwsDaoException{ try{ if(conn!=null&&!conn.getAutoCommit()){ conn.rollback(); conn.setAutoCommit(true); }else{ if(conn==null){ thrownewDaoException("connectionnotopened!"); }else{ thrownewDaoException("firstbeginthenrollbackplease!"); } } }catch(SQLExceptione){ thrownewDaoException("cannotrollbacktransaction!",e); } } }
第二種我們在使用DAO的實例中介紹如何使用(@TODO)
新建兩個DAO,做不同的操作,使用JTA保證事務完整。
查詢方法
查詢方法也許是CommonDao最常用的方法,查詢方法需要將數據庫的結果返回給畫面。返回值我們一般不使用ResultSet,因為ResultSet依賴于Connection,如果Connection關閉,ResultSet將不再有效,所以我們通常將ResultSet轉變為一個List之后返回。
在說明查詢方法之前,我們先說說如何將數據庫中的內容放在List中,我們使用一個List表示一個查詢結果集合,使用一個Map表示集合中的一行,Map的key表示數據庫表的字段名字,Value表示數據庫字段的內容。代碼為:
privateListconvert(ResultSetrs)throwsDaoException{ //recordlist ListretList=newArrayList(); try{ ResultSetMetaDatameta=rs.getMetaData(); //columncount intcolCount=meta.getColumnCount(); //eachrecord while(rs.next()){ MaprecordMap=newHashMap(); //eachcolumn for(inti=1;i<=colCount;i++){ //columnname Stringname=meta.getColumnName(i); //columnvalue Objectvalue=rs.getObject(i); //addcolumntorecord recordMap.put(name,value); } //adrecordtolist retList.add(recordMap); } }catch(SQLExceptionex){ thrownewDaoException("cannotconvertresultsettolistofmap",ex); } returnretList; }
為了避免Sql注入的安全問題,我們通常使用PreparedStatement,在使用PreparedStatement的時候涉及到如何將傳入參數設置到PreparedStatement上面,參看以下的共通方法:
privatevoidapply(PreparedStatementpstmt,Listparams)throwsDaoException{ try{ //ifparamsexist if(params!=null&¶ms.size()>0){ //parametersiterator Iteratorit=params.iterator(); //parameterindex intindex=1; while(it.hasNext()){ Objectobj=it.next(); //ifnullset"" if(obj==null){ pstmt.setObject(index,""); }else{ //elsesetobject pstmt.setObject(index,obj); } //nextindex index++; } } }catch(SQLExceptionex){ thrownewDaoException("cannotapplyparameter",ex); } } 接著我們繼續說我們的查詢方法,有了上述兩個方法,我們的查詢方法就非常簡單了: publicListquery(Stringsql,Listparams)throwsDaoException{ Listresult=null; PreparedStatementpstmt=null; ResultSetrs=null; try{ pstmt=conn.prepareStatement(sql); this.apply(pstmt,params); rs=pstmt.executeQuery(); result=this.convert(rs); }catch(SQLExceptionex){ thrownewDaoException("cannotexecutequery",ex); }finally{ if(rs!=null){ try{ rs.close(); }catch(SQLExceptione){ //nothing } } if(pstmt!=null){ try{ pstmt.close(); }catch(SQLExceptione){ //nothing } } } returnresult; }
特殊的查詢方法(返回單值)
有時候為了方便使用,我們需要返回單值的產尋方法,例如selectmax(id)fromtable_a,selectcount(id)fromtable_b等。以下的代碼使用了上述通用的查詢方法,代碼為:
publicObjectqueryOne(Stringsql,Listparams)throwsDaoException{ Listlist=this.query(sql,params); if(list==null||list.size()==0){ thrownewDaoException("datanotexist"); }else{ Maprecord=(Map)list.get(0); if(record==null||record.size()==0){ thrownewDaoException("datanotexist"); }else{ returnrecord.values().toArray()[0]; } } }
更新,刪除,插入方法
由于在JDBC中這三個方法都是用了一個execute完成,所以這里我們也使用一個方法來完成這些功能。代碼為:
publicintexecute(Stringsql,Listparams)throwsDaoException{ intret=0; PreparedStatementpstmt=null; try{ pstmt=conn.prepareStatement(sql); this.apply(pstmt,params); ret=pstmt.executeUpdate(); }catch(SQLExceptionex){ thrownewDaoException("",ex); }finally{ if(pstmt!=null){ try{ pstmt.close(); }catch(SQLExceptione){ //nothing. } } } returnret; }
批處理方法(查詢)
有些時候為了便于操作,需要一次查詢多條SQL語句,我們稱之為批處理,實現參看以下方法,其中為了和query方法做區分,將參數和返回值都改為了數組形式。
publicList[]queryBatch(String[]sqlArray,List[]paramArray)throwsDaoException{ Listrets=newArrayList(); if(sqlArray.length!=paramArray.length){ thrownewDaoException("sqlsizenotequalparametersize"); }else{ for(inti=0;iStringsql=sqlArray[i]; Listparam=paramArray[i]; Listret=this.query(sql,param); rets.add(ret); } return(List[])rets.toArray(); } }
批處理方法(更新)
有些時候需要一次更新多條Sql語句,為了便于操作,添加了批處理更新操作,參看以下代碼,為了和更新方法區分,將參數和返回值都改為了數組形式。
publicint[]executeBatch(String[]sqlArray,List[]paramArray)throwsDaoException{ Listrets=newArrayList(); if(sqlArray.length!=paramArray.length){ thrownewDaoException("sqlsizenotequalparametersize"); }else{ for(inti=0;iintret=this.execute(sqlArray[i],paramArray[i]); rets.add(newInteger(ret)); } int[]retArray=newint[rets.size()]; for(inti=0;iretArray[i]=((Integer)rets.get(i)).intValue(); } returnretArray; } }
資源釋放
由于CommonDao有一個Connection的屬性,且Connection屬于稀缺資源,所以在CommonDao不需要在使用的時候需要顯示的關閉Connection。代碼如下:
publicvoidclose()throwsDaoException{ try{ if(conn!=null&&conn.getAutoCommit()){ conn.close(); }else{ if(conn==null){ thrownewDaoException("cannotclosenullconnection,firstnewthenclose"); }else{ thrownewDaoException("transactionisrunning,rollbakcorcommitbeforcloseplease."); } } }catch(SQLExceptionex){ thrownewDaoException("Cannotclosecommondao"); } }
JDBC工具類(JDBCUtilClass)
在上述的代碼中我們看到有很多的無用的處理,例如:
if(pstmt!=null){ try{ pstmt.close(); }catch(SQLExceptione){ //nothing. } }
為什么要有這些處理呢?說先這些處理發生的位置都是在正常處理完成之后,這些處理(例如pstmt.close())即使失敗也沒有影響,這個時候我們需要做上述的無用處理,這正是JDBCAPI的一個小小的瑕疵。我們通常使用一個特殊的靜態工具來來做補充,例如:
packagecom.jpleasure.jdbc.dao; importjava.sql.Connection; importjava.sql.PreparedStatement; importjava.sql.ResultSet; importjava.sql.SQLException; publicclassJDBCUtil{ publicvoidsafelyClose(Connectionconn){ if(conn!=null){ try{ conn.close(); }catch(SQLExceptione){ // } } } publicvoidsafelyClose(PreparedStatementpstmt){ if(pstmt!=null){ try{ pstmt.close(); }catch(SQLExceptione){ // } } } publicvoidsafelyClose(ResultSetrs){ if(rs!=null){ try{ rs.close(); }catch(SQLExceptione){ // } } } }
JDBC DAO中異常處理
也許細心的你已經發現了一個問題,為什么所有拋出異常的地方我們都是將SQLException包裝在了DaoException之內拋出呢,為什么不直接拋出SQLException呢?有兩個原因,***,可以細化,分類Exception拋出合適的異常,添加合適的消息,第二,隔離和Dao和業務邏輯的耦合,可以方便的修改Dao層而不會影響到業務邏輯層。另外需要注意,DaoExcetion中可以包含SQLException,這個時候可以為客戶提供更詳細的錯誤信息,例如ORA-12524等內容,但是很少見到。
packagecom.jpleasure.jdbc.dao; publicclassDaoExceptionextendsException{ publicDaoException(){ super(); } publicDaoException(Stringmessage,Throwablecause){ super(message,cause); } publicDaoException(Stringmessage){ super(message); } publicDaoException(Throwablecause){ super(cause); } }
感謝各位的閱讀,以上就是“JDBC DAO的設計理念是什么”的內容了,經過本文的學習后,相信大家對JDBC DAO的設計理念是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。