<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評(píng)論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

    一勞永逸的數(shù)據(jù)庫編碼解決方案

      問題提出

      現(xiàn)在幾乎所有的應(yīng)用系統(tǒng)都無法避免使用數(shù)據(jù)庫系統(tǒng)。在JAVA世界里訪問數(shù)據(jù)庫是一件非常輕松的事情,JDBC為JAVA應(yīng)用程序訪問數(shù)據(jù)庫提供了一個(gè)統(tǒng)一的接口,通過使用JDBC接口開發(fā)者無需關(guān)心系統(tǒng)最終采用哪種數(shù)據(jù)庫,因?yàn)镴DBC僅僅是定義了訪問幾個(gè)JAVA的接口類,具體的實(shí)現(xiàn)是由數(shù)據(jù)庫廠商提供的,這種做法其實(shí)與其他數(shù)據(jù)庫連接方式例如ODBC是類似的。但是在實(shí)際的應(yīng)用過程中,開發(fā)者發(fā)現(xiàn)離JDBC設(shè)計(jì)的初衷還是有一定距離,就比如說在存儲(chǔ)字符串時(shí)的編碼問題,我想很多開發(fā)者都會(huì)遇見這個(gè)問題,倒不是因?yàn)檎f解決它有什么技術(shù)方面的難度,而是它的的確確非常繁瑣。我們必須在每次寫入或者讀出字符串的時(shí)候進(jìn)行編碼和反編碼處理;或者說我們可以寫一個(gè)方法可以進(jìn)行編碼處理的,但又必須在每次數(shù)據(jù)庫操作的時(shí)候調(diào)用,雖然調(diào)用很簡(jiǎn)單,可是我非得這樣嗎?要是忘了編碼那又要DEBUG了。當(dāng)然你可能覺得這并沒有什么,或者你可能很勤快,喜歡寫大量重復(fù)的代碼,可是你難道沒有覺得這種繁瑣的工作正在浪費(fèi)你過于寶貴的青春?jiǎn)幔客V鼓愕逆I盤輸入,讓我們來解決這個(gè)問題吧!

      解決思路

      在傳統(tǒng)的應(yīng)用程序中數(shù)據(jù)庫操作部分我們可以想象成兩層,如圖所示:一個(gè)是數(shù)據(jù)庫的"連接池",另外一個(gè)業(yè)務(wù)數(shù)據(jù)操作層。在這里數(shù)據(jù)庫的連接池是廣義的,你可以把JDBC中的DriverManager也當(dāng)成是連接池,具體的意思就是我們可以通過這層來獲取到指定數(shù)據(jù)庫的連接而不去關(guān)心它是怎么獲取的。如果這個(gè)時(shí)候數(shù)據(jù)庫系統(tǒng)(有如Informix,SQL Server)要求對(duì)字符串進(jìn)行轉(zhuǎn)碼才能存儲(chǔ)(例如最常見的GBK->ISO8859_1轉(zhuǎn)碼),那我們就必須在業(yè)務(wù)數(shù)據(jù)操作層來進(jìn)行,這樣有多少業(yè)務(wù)數(shù)據(jù)操作我們就要做多少編碼轉(zhuǎn)碼的工作,太麻煩了,代碼中充斥中大量重復(fù)的內(nèi)容。本文提出的解決方案就是利用對(duì)獲取到的數(shù)據(jù)庫連接實(shí)例進(jìn)行二次封裝,也就是在數(shù)據(jù)庫連接池與業(yè)務(wù)數(shù)據(jù)操作層之間加入了連接封裝層,當(dāng)然了,我們也完全可以直接將連接封裝集成到數(shù)據(jù)庫連接池內(nèi)部。關(guān)于連接池的實(shí)現(xiàn)請(qǐng)參照我的另外一篇文章《使用JAVA動(dòng)態(tài)代理實(shí)現(xiàn)數(shù)據(jù)庫連接池》


    圖一

      我們知道進(jìn)行編碼和轉(zhuǎn)碼工作都是集中在JDBC的兩個(gè)接口PreparedStatement和ResultSet上進(jìn)行的,主要涉及PreparedStatement的setString方法以及ResultSet的getString方法。前面我們講過需要加入一個(gè)連接封裝層來對(duì)數(shù)據(jù)庫連接實(shí)例進(jìn)行二次封裝,但是怎么通過這個(gè)封裝來改變PreparedStatement和ResultSet這兩個(gè)接口的行為呢?這個(gè)問題其實(shí)也很簡(jiǎn)單,因?yàn)镻reparedStatement接口必須通過Connection接口來獲取實(shí)例,而ResultSet接口又必須從Statement或者PreparedStatement接口來獲取實(shí)例,有了這樣的級(jí)聯(lián)關(guān)系,問題也就迎刃而解了。還是利用我在文章《使用JAVA動(dòng)態(tài)代理實(shí)現(xiàn)數(shù)據(jù)庫連接池》中使用的動(dòng)態(tài)接口代理技術(shù)。首先我們?cè)O(shè)計(jì)Connection接口的代理類_Connection,這個(gè)代理類接管了Connection接口中所有可能獲取到Statement或者PreparedStatement接口實(shí)例的方法,例如:prepareStatement和createStatement。改變這兩個(gè)方法使之返回的是經(jīng)過接管后的Statement或者PreparedStatement實(shí)例。通過對(duì)于Statement接口也有相應(yīng)的代理類_Statement,這個(gè)代理類接管用于獲取ResultSet接口實(shí)例的所有方法,包括對(duì)setString方法的接管以決定是否對(duì)字符串進(jìn)行編碼處理。對(duì)于接口ResultSet的接管類_ResultSet就相應(yīng)的比較簡(jiǎn)單,它只需要處理getString方法即可。

      關(guān)鍵代碼

      前面我們大概介紹了這個(gè)解決方案的思路,下面我們給出關(guān)鍵的實(shí)現(xiàn)代碼包括Connection的代理類,Statement的代理類,ResultSet的代理類。這些代碼是在原來關(guān)于數(shù)據(jù)庫連接池實(shí)現(xiàn)的基礎(chǔ)上進(jìn)行擴(kuò)充使之增加對(duì)自動(dòng)編碼處理的功能。有需要源碼打包的可以通過電子郵件跟我聯(lián)系。

    _Connection.java

    /*
    ?* Created on 2003-10-23 by Liudong
    ?*/
    package lius.pool;
    import java.sql.*;
    import java.lang.reflect.*;

    /*
    ?*
    ?* 數(shù)據(jù)庫連接的代理類
    ?* @author Liudong
    ?*/
    ?class _Connection implements InvocationHandler{
    ?private Connection conn = null;
    ?private boolean coding = false;
    ?//指定是否進(jìn)行字符串轉(zhuǎn)碼操作
    ?_Connection(Connection conn, boolean coding){
    ??this.conn = conn;
    ??this.coding = coding;
    ??initConnectionParam(this.conn);
    ?
    ?}
    ?
    ?/**?
    ? * Returns the conn.?
    ? * @return Connection?
    ? */
    ?
    ?public Connection getConnection() {
    ??Class[] interfaces = conn.getClass().getInterfaces();
    ??if(interfaces==null||interfaces.length==0){
    ???interfaces = new Class[1];
    ???interfaces[0] = Connection.class;
    ??
    ??}
    ?
    ??Connection conn2 = (Connection)Proxy.newProxyInstance(?conn.getClass().getClassLoader(), interfaces,this);
    ??return conn2;
    ?
    ?}
    ?
    ?/**?
    ? * @see java.lang.reflect.InvocationHandler#invoke?
    ? */
    ?public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {?
    ??String method = m.getName();
    ??//調(diào)用相應(yīng)的操作
    ??Object obj = null;
    ??try{
    ???obj = m.invoke(conn, args);
    ???//接管用于獲取語句句柄實(shí)例的方法
    ???if((CS.equals(method)||PS.equals(method))&&coding)
    ????return new _Statement((Statement)obj,true).getStatement();
    ??
    ??} catch(InvocationTargetException e) {
    ???throw e.getTargetException();
    ??}
    ??return obj;
    ?}
    ?
    ?private final static String PS = "prepareStatement";
    ?private final static String CS = "createStatement";
    }


    _Statement.java

    /*
    ?* Created on 2003-10-23 by Liudong
    ?*/
    ?
    package lius.pool;
    import java.sql.*;
    import java.lang.reflect.*;

    /**
    ?* 數(shù)據(jù)庫語句對(duì)象實(shí)例的代理類
    ?* @author Liudong
    ?*/
    class _Statement implements InvocationHandler{?
    ?private Statement statement ; //保存所接管對(duì)象的實(shí)例?
    ?private boolean decode = false; //指定是否進(jìn)行字符串轉(zhuǎn)碼?

    ?public _Statement(Statement stmt,boolean decode) {?
    ??this.statement = stmt;
    ??this.decode = decode;
    ?}
    ?
    ?/**?
    ? * 獲取一個(gè)接管后的對(duì)象實(shí)例?
    ? * @return?
    ? */
    ?public Statement getStatement() {
    ??Class[] interfaces = statement.getClass().getInterfaces();
    ??if(interfaces==null||interfaces.length==0){?
    ???interfaces = new Class[1];
    ???interfaces[0] = Statement.class;
    ??}?
    ??Statement stmt = (Statement)Proxy.newProxyInstance(???
    ???statement.getClass().getClassLoader(),?
    ???interfaces,this);
    ??return stmt;
    ?
    ?}
    ?
    ?/**?
    ? * 方法接管?
    ? */
    ?public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    ??String method = m.getName(); //接管setString方法?
    ??if(decode && SETSTRING.equals(method)) {
    ???try{
    ????String param = (String)args[1];
    ????if(param!=null)
    ?????param = new String(param.getBytes(),"8859_1");
    ????return m.invoke(statement,new Object[]{args[0],param});
    ???} catch(InvocationTargetException e){
    ????throw e.getTargetException();
    ????????
    ???}??
    ??}
    ??
    ??//接管executeQuery方法
    ??
    ??if(decode && EXECUTEQUERY.equals(method)){
    ???try{
    ????ResultSet rs = (ResultSet)m.invoke(statement,args);
    ????return new _ResultSet(rs,decode).getResultSet();
    ???
    ???}catch(InvocationTargetException e){
    ????throw e.getTargetException();
    ????}??
    ??}
    ??
    ??try{
    ???return m.invoke(statement, args);
    ??} catch(InvocationTargetException e) {
    ???throw e.getTargetException();
    ???}?
    ?}
    ?//兩個(gè)要接管的方法名
    ?
    ?private final static String SETSTRING = "setString";
    ?private final static String EXECUTEQUERY = "executeQuery";
    }


    _ResultSet.java

    /*
    ?* Created on 2003-10-23 by Liudong
    ?*/
    ?
    package lius.pool;
    import java.sql.ResultSet;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    /**
    ?* 數(shù)據(jù)庫結(jié)果集的代理類
    ?* @author Liudong
    ?*/
    ?class _ResultSet implements InvocationHandler{?
    ?private ResultSet rs = null;
    ?private boolean decode = false;
    ?
    ?public _ResultSet(ResultSet rs,boolean decode) {
    ??this.rs = rs;
    ??this.decode = decode;
    ?}
    ?
    ?public ResultSet getResultSet(){?
    ??Class[] interfaces = rs.getClass().getInterfaces();
    ??if(interfaces==null||interfaces.length==0){
    ???interfaces = new Class[1];
    ???interfaces[0] = ResultSet.class;??
    ??}
    ?
    ??ResultSet rs2 = (ResultSet)Proxy.newProxyInstance(rs.getClass().getClassLoader(),interfaces,this);
    ??return rs2;
    ?
    ?}

    ?/**?
    ? * 結(jié)果getString方法?
    ? */
    ?public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {?
    ??String method = m.getName();
    ??if(decode && GETSTRING.equals(method)){
    ???try{
    ????String result = (String)m.invoke(rs,args);
    ????if(result!=null)?????
    ?????return new String(result.getBytes("8859_1"));
    ????return null;
    ???
    ???}catch(InvocationTargetException e){
    ????throw e.getTargetException();
    ????}
    ???
    ??}?
    ??
    ??try{
    ???return m.invoke(rs, args);
    ??}catch(InvocationTargetException e){
    ???throw e.getTargetException();
    ??}
    ?}
    ?
    ?private final static String GETSTRING = "getString";

    }

      現(xiàn)在我們已經(jīng)把三個(gè)接口的代理類做好了,下一步就是怎么來使用這三個(gè)類。其實(shí)對(duì)于使用者來講并不需要關(guān)心三個(gè)類,只需要了解_Connection就可以了,因?yàn)榱硗鈨蓚€(gè)是_Connection直接調(diào)用的。為了使用_Connection我們必須傳入兩個(gè)參數(shù),第一個(gè)是數(shù)據(jù)庫實(shí)際的數(shù)據(jù)庫連接實(shí)例,另外一個(gè)是布爾值代表是否進(jìn)行轉(zhuǎn)碼處理。我們必須先通過實(shí)際的情況獲取到數(shù)據(jù)庫連接后再傳入_Connection的構(gòu)造函數(shù)作為參數(shù),下面例子告訴你如何來使用_Connection這個(gè)類:

      Connection conn = getConnection(); //獲取數(shù)據(jù)庫連接
      boolean coding = false; //從配置或者其他地方讀取是否進(jìn)行轉(zhuǎn)碼的配置?
      //接管數(shù)據(jù)庫連接實(shí)例?
      _Connection _conn = new _Connection(conn,coding);
      //獲得接管后的數(shù)據(jù)庫連接實(shí)例,以后直接使用conn2而不是conn?
      Connection conn2 = _conn.getConnection();

      因?yàn)閷?duì)一個(gè)應(yīng)用系統(tǒng)來講,數(shù)據(jù)庫連接的獲取必然有統(tǒng)一的方法,在這個(gè)方法中加入對(duì)連接的接管就可以一勞永逸的解決數(shù)據(jù)庫的編碼問題。

      性能比較

      功能沒有問題了,開發(fā)者接下來就會(huì)關(guān)心性能的問題,因?yàn)樵谶M(jìn)行一些對(duì)響應(yīng)速度要求很高或者大數(shù)據(jù)量的處理情況下性能就成為一個(gè)非常突出的問題。由于JAVA中的動(dòng)態(tài)接口代理采用的是反射(Reflection)機(jī)制,同時(shí)又加入我們自己的一些代碼例如方法名判斷,字符串轉(zhuǎn)碼等操作因此在性能上肯定比不上直接使用沒有經(jīng)過接管的數(shù)據(jù)庫連接。但是這點(diǎn)性能上的差別是不是我們可以忍受的呢,為此我做了一個(gè)試驗(yàn)對(duì)二者進(jìn)行了比較:

      測(cè)試環(huán)境簡(jiǎn)單描述:

      使用ACCESS數(shù)據(jù)庫,建兩張結(jié)構(gòu)一樣的表,計(jì)算從獲取連接后到插入數(shù)據(jù)完畢后的時(shí)間差,兩個(gè)程序(直連數(shù)據(jù)庫和使用連接接管)都進(jìn)行的字符串的轉(zhuǎn)碼操作。

      測(cè)試結(jié)果:

    插入記錄數(shù) 直連數(shù)據(jù)庫程序耗時(shí) 單位:ms 使用連接接管程序耗時(shí) 性能比較
    1000 2063 2250 9.0%
    5000 8594 8359 -2.7%
    10000 16750 17219 2.8%
    15000 22187 23000 3.6%
    20000 27031 27813 2.9%

      從上面這張測(cè)試結(jié)果表中來看,二者的性能的差別非常小,盡管在兩萬條數(shù)據(jù)的批量插入的時(shí)候時(shí)間差別也不會(huì)多于一秒鐘,這樣的結(jié)果應(yīng)該說還是令人滿意的,畢竟為了程序良好的結(jié)構(gòu)有時(shí)候犧牲一點(diǎn)點(diǎn)性能還是值得的。

      本文算是我之前文章《使用JAVA動(dòng)態(tài)代理實(shí)現(xiàn)數(shù)據(jù)庫連接池》中提出的數(shù)據(jù)庫連接池實(shí)現(xiàn)的進(jìn)一步完善,同樣使用動(dòng)態(tài)接口代理的技術(shù)來解決數(shù)據(jù)庫編碼的問題。JAVA的這個(gè)高級(jí)技術(shù)可以用來解決許多實(shí)際中非常棘手的問題,就像本文提到的編碼問題的處理以及數(shù)據(jù)庫連接池的實(shí)現(xiàn),同時(shí)在WEB開發(fā)框架的實(shí)現(xiàn)上也有非常大的作為。歡迎對(duì)這方面感興趣的朋友來信共同來研究。

    from: http://www.javafan.net/article/20041212111952983.html

    posted on 2006-12-03 13:22 weidagang2046 閱讀(442) 評(píng)論(0)  編輯  收藏 所屬分類: JavaDatabase

    主站蜘蛛池模板: 国内一级一级毛片a免费| 免费无码VA一区二区三区| 嫩草影院免费观看| 91情国产l精品国产亚洲区| 天天拍拍天天爽免费视频| 99人中文字幕亚洲区| 好紧我太爽了视频免费国产| 国产亚洲av片在线观看播放| 男女一边桶一边摸一边脱视频免费| 亚洲日本中文字幕一区二区三区| 夜夜爽妓女8888视频免费观看| 亚洲一区二区三区国产精品| 中文字幕在线免费看线人| 在线精品亚洲一区二区小说 | 亚洲暴爽av人人爽日日碰| 毛片a级三毛片免费播放| 亚洲国产精品嫩草影院| 四虎影视永久免费观看| 国产精品九九久久免费视频| 亚洲精品V欧洲精品V日韩精品 | 99爱在线观看免费完整版| 久久亚洲精品成人无码网站| 97人妻无码一区二区精品免费| 亚洲高清有码中文字| 国产色爽免费视频| 国产黄在线播放免费观看| 亚洲av网址在线观看| 精品国产免费人成电影在线观看 | 亚洲A∨精品一区二区三区| a级片免费在线播放| 91亚洲va在线天线va天堂va国产 | 国产精品免费久久久久电影网| 亚洲国产精品成人精品无码区在线| 中文字幕免费在线看| 亚洲福利一区二区| 日韩在线免费看网站| 免费一区二区无码东京热| 亚洲宅男天堂a在线| 亚洲国产日韩在线观频| 花蝴蝶免费视频在线观看高清版| 亚洲一区在线视频|