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

          常用鏈接

          留言簿(1)

          隨筆檔案(8)

          文章檔案(1)

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          JDBC針對條件個數不定的查詢,一般的做法是直接拼裝查詢字符串,但是這樣做無法抵御注入攻擊,還是要封裝才安全,但是封裝對條件個數、類型不定,是很有困難的,最近終于找到了解決辦法:
          public class queryDAOImpl implements queryDAO {
              
          /**
               * 動態拼裝查詢
               * 
          @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();   //儲存查詢條件
                  Vector<String> t2 = new Vector<String>();   //儲存查詢條件類型
                  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();    //取得數據庫連接
                      pstmt = con.prepareStatement(sql);
                      
          int paramNum = t.size();    //查詢條件數量
                      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();
                      
          //以字段名為鍵,以字段值為值,將查詢結果存入Map,再裝進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) 評論(8)  編輯  收藏

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

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

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

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

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

          代碼片段:

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

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

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

          在控制臺輸出的調試信息為:

          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:鄧%
          ------------------------------

          在控制臺輸出的程序結果為:

          [ 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 = 鄧遠辰, onlinecount = 429, birthday = null, loginname = ChengCheng, loginpassword = chengchengpwd, email = chengchengdream@gmail.com, accountstate = true ]

          我也非常支持重復造輪子的行為,我上面那個泛型DAO的find方法實現是這樣的:

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

          這段時間一直也在自己琢磨動態SQL的拼裝問題,但總是顯得對復雜SQL的支持還不夠。也許還是只有單獨封裝個高內聚的SQL查詢條件包裝器才能達到效果。

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

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

          // 數據庫連接對象
          Connection conn = null;
          // SQL命令執行對象
          PreparedStatement ps = null;
          // 結果集對象
          ResultSet rs = null;
          // JDBC事務管理對象
          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);
          }

          }  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 望奎县| 招远市| 西贡区| 平原县| 克什克腾旗| 正宁县| 宁明县| 县级市| 交口县| 连云港市| 桐柏县| 绵阳市| 双柏县| 安乡县| 韶关市| 东海县| 张家口市| 都安| 德保县| 西林县| 寻乌县| 锡林郭勒盟| 磴口县| 郁南县| 贵溪市| 崇信县| 呼和浩特市| 开鲁县| 辉县市| 固原市| 西丰县| 光泽县| 济南市| 彭阳县| 建平县| 宁波市| 通化县| 集安市| 乌审旗| 沿河| 曲周县|