隨筆 - 3  文章 - 8  trackbacks - 0
          <2009年1月>
          28293031123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿(1)

          隨筆檔案(8)

          文章檔案(1)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          JDBC針對(duì)條件個(gè)數(shù)不定的查詢,一般的做法是直接拼裝查詢字符串,但是這樣做無法抵御注入攻擊,還是要封裝才安全,但是封裝對(duì)條件個(gè)數(shù)、類型不定,是很有困難的,最近終于找到了解決辦法:
          public class queryDAOImpl implements queryDAO {
              
          /**
               * 動(dòng)態(tài)拼裝查詢
               * 
          @param mq 封裝查詢條件的類
               * 
          @return
               * 
          @throws java.sql.SQLException
               
          */
              
          public List<Map> queryAll(MnewsQ mq) throws SQLException {
                  List
          <Map> v = new Vector<Map>();
                  Connection con 
          = null;
                  PreparedStatement pstmt 
          = null;
                  ResultSet rs 
          = null;
                  Vector t 
          = new Vector();   //儲(chǔ)存查詢條件
                  Vector<String> t2 = new Vector<String>();   //儲(chǔ)存查詢條件類型
                  String sql = "SELECT * FROM mnews WHERE mnews.sc=0";
                  
          //如果提交了查詢條件gj (int型)
                  if (mq.getGj() > 0) {
                      sql 
          = sql + " AND gj=?";
                      t.add(mq.getGj());
                      t2.add(
          "int");
                  }
                  
          //如果提交了查詢條件gjz4 (String型)
                  if (mq.getGjz4() != null && mq.getGjz4().length() > 0) {
                      sql 
          = sql + " AND gjz4=?";
                      t.add(mq.getGjz4());
                      t2.add(
          "String");
                  }
                  
          try {
                      con 
          = ConnectionMannagerJNDI.getCon();    //取得數(shù)據(jù)庫(kù)連接
                      pstmt = con.prepareStatement(sql);
                      
          int paramNum = t.size();    //查詢條件數(shù)量
                      for (int i=0; i<paramNum; i++) {
                          
          if ("int".equals(t2.get(i).toString())) {
                              pstmt.setInt(i 
          + 1 + 2, Integer.parseInt(t.get(i).toString()));
                          } 
          else if ("String".equals(t2.get(i).toString())) {
                              pstmt.setString(i 
          + 1 + 2, t.get(i).toString());
                          }
          //Date、float等類型依此類推
                      }
                      rs 
          = pstmt.executeQuery();
                      
          //以字段名為鍵,以字段值為值,將查詢結(jié)果存入Map,再裝進(jìn)Vector
                      ResultSetMetaData rsmd = rs.getMetaData();
                      
          int columnNum = rsmd.getColumnCount();
                      
          while (rs.next()) {
                          Map map 
          = new HashMap();
                          
          for (int i=1; i<=columnNum; i++) {
                              map.put(rsmd.getColumnName(i),rs.getObject(i));
                          }
                          v.add(map);
                      }
                  } 
          finally {
                      ConnectionMannagerJNDI.releaseConnection(rs, pstmt, 
          null, con);
                  }
                  
          return v;
              }
          }


          posted on 2009-01-26 18:38 cccp21 閱讀(5456) 評(píng)論(8)  編輯  收藏

          FeedBack:
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-01-26 19:00 銀河使者
          條件是動(dòng)態(tài)的也可以用查詢參數(shù),用java.sql.PreparedStatement就可以。樓主只是做了個(gè)封裝,不過個(gè)人認(rèn)為ResultSet本身也可以根據(jù)列名來定位列,而再把每行放在一個(gè)Map中,再把結(jié)果集放在Vector中,感覺有些多此一舉。  回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-01-29 11:31 cccp21
          @銀河使者
          請(qǐng)注意,是條件的個(gè)數(shù)是動(dòng)態(tài)的,PreparedStatement只是讓開發(fā)者設(shè)置參數(shù)值,但是參數(shù)個(gè)數(shù)還是固定的。
          把每行放在一個(gè)Map中,再把結(jié)果集放在Vector中,是為了把JDBC與應(yīng)用分離開,我開始的時(shí)候也和你一樣,后來才聽別人指出這一點(diǎn)的。  回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-01-29 19:26 銀河使者
          @ cccp21
          不討論P(yáng)reparedStatement了,要想動(dòng)態(tài),一般也是采用你的方法,即動(dòng)態(tài)生成帶SQL參數(shù)的SQL語(yǔ)句,以及通過循環(huán)調(diào)用setter方法來設(shè)置參數(shù)的值,這就是動(dòng)態(tài),所有的動(dòng)態(tài)基本都是這么弄的。
          我是說第二個(gè)問題,在查詢后,又使用Map和Vector再對(duì)數(shù)據(jù)進(jìn)行封裝,是不是有些耗資源。要注意哦,如果是Web程序,可不是一個(gè)用戶或幾個(gè)用戶在訪問程序啊,要是門戶類型的系統(tǒng),可能在一分鐘之內(nèi)就會(huì)有數(shù)以萬計(jì)的用戶來訪問。這時(shí)就要建立多達(dá)數(shù)萬個(gè)Map和Vector,服務(wù)器會(huì)掛的。當(dāng)然,如果用戶在可控范圍內(nèi),這么做是沒有問題的。但是可以封裝得更高級(jí)一些,如返回的這些數(shù)據(jù)直接可以被某些用于顯示的組件使用,如使用json格式的組件。這根據(jù)具體情況而定。
          是的,使用Map和Vector可以從邏輯上將JDBC和應(yīng)用進(jìn)行分離。但個(gè)人認(rèn)為要想分離,可以從數(shù)據(jù)層及應(yīng)用邏輯層進(jìn)行分離更好。也就是有數(shù)據(jù)層提供相應(yīng)的訪問數(shù)據(jù)的方法,如獲得指定用戶的保存在數(shù)據(jù)庫(kù)中的密碼。而應(yīng)用邏輯層一般不直接訪問數(shù)據(jù)庫(kù),可以訪問數(shù)據(jù)層中的方法進(jìn)行邏輯處理。
          雖然將數(shù)據(jù)放在Map是Vector中是更抽象了,但JDBC本身就是抽象的,在業(yè)務(wù)邏輯層直接訪問ResultSet并沒有什么不妥,因?yàn)椴还芎笈_(tái)用的是什么數(shù)據(jù)庫(kù),從ResultSet中獲得數(shù)據(jù)的方法都是一樣的。如果非想要跨數(shù)據(jù)庫(kù),不如直接用hibernate。  回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-01-29 19:31 銀河使者
          封裝本是面象對(duì)象最值得稱道的地方,如果封裝得太多有時(shí)得不償失。個(gè)人認(rèn)為在業(yè)務(wù)邏輯層,只要不直接涉及到SQL就可以,因?yàn)镾QL本身是和數(shù)據(jù)庫(kù)相關(guān)的,而業(yè)務(wù)邏輯層應(yīng)和數(shù)據(jù)庫(kù)不相關(guān)。但對(duì)于JDBC中的對(duì)象的使用上來說,一般也是和數(shù)據(jù)庫(kù)不相關(guān)的(至少對(duì)于象SQL Server、oracle、db2、mysql這樣的數(shù)據(jù)庫(kù)來說是這樣的)。

          結(jié)論:數(shù)據(jù)訪問層直接和數(shù)據(jù)庫(kù)相關(guān),可以涉及任何的SQL語(yǔ)句。業(yè)務(wù)邏輯層與數(shù)據(jù)庫(kù)無關(guān),在該層直接訪問數(shù)據(jù)層,不能直接在業(yè)務(wù)邏輯層編寫SQL語(yǔ)句,但可以使用JDBC的相關(guān)對(duì)象。  回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-01-31 10:57 cccp21
          @銀河使者
          我覺得這個(gè)思路不錯(cuò)是因?yàn)檫@樣可以在同一個(gè)方法中建立、關(guān)閉數(shù)據(jù)庫(kù)連接。之前曾經(jīng)在關(guān)閉數(shù)據(jù)庫(kù)連接這個(gè)問題上吃過虧。
          如果在應(yīng)用中直接訪問ResultSet,關(guān)閉數(shù)據(jù)庫(kù)連接有點(diǎn)麻煩——如果之前就關(guān)閉了數(shù)據(jù)庫(kù)連接,那么應(yīng)用在ResultSet中就訪問不到數(shù)據(jù),而不關(guān)閉的話,就要在應(yīng)用完成后關(guān)閉數(shù)據(jù)庫(kù)連接,這里就會(huì)有些混亂。你有什么好主意呢?  回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-01-31 21:05 銀河使者
          @ cccp21
          在使用ResultSet時(shí)當(dāng)然不能關(guān)閉Connection,不知道你為什么會(huì)在使用ResultSet之間關(guān)閉Connection,這個(gè)Connection是共享的?還是用的什么其他方式。為了提高效率,可以使用連接池,一般每個(gè)Connection都是線程獨(dú)享的,用完了再還給連接池。以上只針對(duì)Web應(yīng)用,如果是桌面引用,建議使用Web Service方式。

          如果想關(guān)閉ResultSet方便些,可以使用jdbc+spring的方式,也就是jdbcTemplate,這個(gè)很方便。基本原理是從連接池中獲得一個(gè)Connection,執(zhí)行一條或多條SQL語(yǔ)句,返回結(jié)果(這個(gè)結(jié)果也是封裝的,和你的方法差不多,但考慮了異常處理),在返回結(jié)果之前,Connection就已經(jīng)關(guān)閉了,關(guān)閉的動(dòng)作是由Spring自動(dòng)完成的。 而對(duì)于用戶來說,只是調(diào)用了JdbcTemplate的一個(gè)方法來獲得查詢結(jié)果。再也不用考慮什么Connection、ResultSet,另人討厭。不過對(duì)于小程序,用什么都行,我還是喜歡直接用jdbc,對(duì)于大一點(diǎn)的程序,尤其對(duì)Web程序,個(gè)人認(rèn)為用jdbc +Spring比較好,如果想更高級(jí)一些,還跨數(shù)據(jù)庫(kù),可以使用hibernate+spring的方式。  回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2009-02-16 21:57 cccp21
          @銀河使者
          你是說在web頁(yè)中接受并使用ResultSet然后再在該頁(yè)中關(guān)閉ResultSet和數(shù)據(jù)庫(kù)連接?
          還是說接受并用完ResultSet后關(guān)閉ResultSet并調(diào)用另一個(gè)函數(shù)關(guān)閉連接?

          一般每個(gè)Connection都是線程獨(dú)享的——那為什么我以前看到很多取數(shù)據(jù)源連接的方法要保證之生成一個(gè)數(shù)據(jù)源對(duì)象?比如:
              private static DataSource ds = null;
              private static Object Lock = new Object();
             
          /**
           * 生成DataSource
           * @return 返回一個(gè)DataSource對(duì)象
           */
              public static DataSource gainDataSource(){
                  try {
                      if(ds == null){
                          synchronized(Lock){
                              if(ds == null){
                                  InitialContext ctx = new InitialContext();
                                  ds = (DataSource)ctx.lookup("數(shù)據(jù)源名");
                              }
                          }
                      }
                  } catch (NamingException e) {e.printStackTrace();}
                  return ds;
              }
          這有什么好處呢?
            回復(fù)  更多評(píng)論
            
          # re: JDBC查詢動(dòng)態(tài)封裝 2010-04-14 01:00 CodingMouse
          要想重復(fù)造輪子就要造得好用一些才行:

          我也造了一個(gè)輪子,下面這樣的效果。

          代碼片段:

          // 構(gòu)建DAO工廠
          AbstractDAOFactory<AccountPOJO, Long> factory = AbstractDAOFactory.newInstance();
          // 構(gòu)建DAO實(shí)例
          IGenericsDAO<AccountPOJO, Long> dao = factory.buildGenericsDAO();

          // 按條件檢索數(shù)據(jù)并自動(dòng)映射成POJO有序集合
          List<AccountPOJO> pojos = dao.find(
          AccountPOJO.class,
          Condition.valueOf("accountid", Condition.LE, 10),
          Condition.and("birthday", Condition.ISNULL),
          Condition.or("accountname", Condition.LIKE, "鄧%"));

          // 在控制臺(tái)輸出結(jié)果
          for (AccountPOJO account : pojos) {
          System.out.println(account);
          }

          在控制臺(tái)輸出的調(diào)試信息為:

          2010-04-14 00:48:58,281 main DEBUG [com.china.codingmouse.cmsdk4j.dao.sql.generator.SQLGenerator]
          [CmSdk4j Auto SQL Generator In 2010-04-14 00:48:58.281]
          SELECT accountid, accountname, onlinecount, birthday, loginname, loginpassword, email, accountstate FROM account WHERE accountid <= ? AND birthday IS NULL OR accountname LIKE ?
          ------------------------------
          2010-04-14 00:48:58,328 main DEBUG [com.china.codingmouse.cmsdk4j.dao.converter.DataTypeConverter]
          [SQL Parameter List Information In 2010-04-14 00:48:58.328]
          No:1
          Type:java.lang.Integer
          Value:10
          ------------------------------
          2010-04-14 00:48:58,343 main DEBUG [com.china.codingmouse.cmsdk4j.dao.converter.DataTypeConverter]
          [SQL Parameter List Information In 2010-04-14 00:48:58.343]
          No:2
          Type:java.lang.String
          Value:鄧%
          ------------------------------

          在控制臺(tái)輸出的程序結(jié)果為:

          [ accountid = 1, accountname = 鄧超, onlinecount = 2203, birthday = 1984-12-26, loginname = CodingMouse, loginpassword = cmsdk4j, email = CodingMouse@gmail.com, accountstate = true ]
          [ accountid = 3, accountname = 李四, onlinecount = 932, birthday = null, loginname = LiSi, loginpassword = lisi123, email = lisi123@126.com, accountstate = false ]
          [ accountid = 7, accountname = 鄧遠(yuǎn)辰, onlinecount = 429, birthday = null, loginname = ChengCheng, loginpassword = chengchengpwd, email = chengchengdream@gmail.com, accountstate = true ]

          我也非常支持重復(fù)造輪子的行為,我上面那個(gè)泛型DAO的find方法實(shí)現(xiàn)是這樣的:

          /**
          * 獲取該類型匹配的模型有序集合。<br><br>
          *
          * @param clz 注冊(cè)返回類型。
          * @param conditions SQL條件列表。
          * @return 該類型匹配的全部模型有序集合。
          * @throws DataAccessException 數(shù)據(jù)訪問異常。
          */
          public List<T> find(Class<? extends Object> clz, Condition... conditions)
          throws DataAccessException {
          return this.findAction.find(clz, conditions);
          }

          這段時(shí)間一直也在自己琢磨動(dòng)態(tài)SQL的拼裝問題,但總是顯得對(duì)復(fù)雜SQL的支持還不夠。也許還是只有單獨(dú)封裝個(gè)高內(nèi)聚的SQL查詢條件包裝器才能達(dá)到效果。

          由于我的DAO抽象基類還是采用的短事務(wù)處理方式,所以,總感覺還是不能完全滿足需求:

          /**
          * 執(zhí)行查詢SQL命令并返回模型有序集合。<br><br>
          *
          * @param clz 注冊(cè)模型類型。
          * @param sql 要執(zhí)行的帶占位符SQL命令字串。
          * @param param SQL參數(shù)列表。
          * @return 泛型模型集合。
          * @throws DataAccessException 數(shù)據(jù)訪問異常。
          */
          public List<Object> executeQuery(
          Class<? extends Object> clz,
          String sql,
          Parameter... param)
          throws DataAccessException {

          // 數(shù)據(jù)庫(kù)連接對(duì)象
          Connection conn = null;
          // SQL命令執(zhí)行對(duì)象
          PreparedStatement ps = null;
          // 結(jié)果集對(duì)象
          ResultSet rs = null;
          // JDBC事務(wù)管理對(duì)象
          DBTransaction trans = null;

          try {
          conn = DB_CONNECTION_POOL.getConnection();
          trans = DBTransaction.begin(conn);
          ps = DataTypeConverter.java2Jdbc(
          conn.prepareStatement(sql),
          param);
          rs = ps.executeQuery();
          List<Object> modelList = new Vector<Object>();
          while(rs.next()) {
          Object model = DataTypeConverter.jdbc2Java(
          clz,
          rs);
          modelList.add(model);
          }
          trans.commit();
          return modelList;
          } catch (Throwable e) {
          trans.rollback();
          logger.error(
          this.getClass().getName(),
          Logger.DIR_INTERNAL,
          null,
          Logger.getStackTrace(e));
          throw new DataAccessException(e);
          } finally {
          this.closeAll(rs, ps, conn, trans);
          }

          }  回復(fù)  更多評(píng)論
            

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 阳新县| 重庆市| 巴彦淖尔市| 鹤壁市| 腾冲县| 德昌县| 大洼县| 贡嘎县| 镇江市| 江北区| 新宾| 阿拉善右旗| 德钦县| 宜兰市| 威宁| 内丘县| 卢龙县| 依安县| 定襄县| 乌拉特中旗| 林西县| 绵阳市| 卢氏县| 永丰县| 昆山市| 万荣县| 泸定县| 阳春市| 开化县| 乌兰县| 长垣县| 金塔县| 丰镇市| 北海市| 长治市| 平阴县| 祥云县| 同德县| 兴和县| 苏尼特右旗| 岱山县|