關於ibatis批量問題-我們到底能走多遠系列(26)


我們到底能走多遠系列(26)

推薦:

    推薦使用 evernote 或其他類似工具瀏覽網頁,現如今,我們大多數的閱讀都是通過現實屏作為窗口實現的,記筆記或者再要一下是非常必要的尤其是看那些技術類的文章時,最近使用 evernote后,發現很不錯,可以在瀏覽器上裝一個插件,遇到好文,就可以方便的記錄下來,然后同步到自己的移動設備上去,evernote沒有免費 的離線,如果不想用流量,可以在wifi的情況下,點開那些文章,加載完畢后關閉,晚上回家就可以離線查看啦!

主題:

關於一些基礎的東西,記錄一下:

1,java中list遍歷時刪除問題:

簡化的for-each循環只是一個語法糖,等價於如下代碼:

for( Iterator<ComType> iter = ComList.iterator(); iter.hasNext();){
    ComType com = iter.next();
    if ( !com.getName().contains("abc")){
        ComList.remove(com);
    }
}

但是同時遍歷和刪除一個特list,是不被支持的。

解決辦法:

使用Iterator遍歷,使用Iterator.remove()刪除你想刪除的元素。

 

for( Iterator<ComType> iter = ComList.iterator(); iter.hasNext();){
    ComType com = iter.next();
    if ( !com.getName().contains("abc")){
        iter.remove();
    }
}

2,關於map遍歷操作問題

反面教材代碼:

        for (Long id : userAlbumMap.keySet()) {
            if (userAlbumMap.get(id) > 0) {//遍歷了一遍map
                UserAndJourneyDO userAndJourney = userMap.get(id);
                userAndJourney.setAlbum(1);
            }
        }

注意注釋部分,以上代碼,我覺得至少遍歷了兩遍map。

改進:

        for(Map.Entry<Long, Integer> entry : userAlbumMap.entrySet()){
               if (entry.getValue() > 0) {//現成使用,不需要遍歷map
                   UserAndJourneyDO userAndJourney = userMap.get(entry.getKey());
                   userAndJourney.setAlbum(1);
               }
        }

 

3,關於iBatis批量處理的問題

問題是這樣出現的:為了提高執行的效率,我們需要在與數據庫建立連接時一次性做竟可能多的事。

為什么要這樣呢? 只要設想一下如果數據庫是分布式部署的話,那么每次連接都要建立socket,這樣的開銷就大了。

比如:我么遇到根據多個用戶id查詢數據時,我們考慮 用sql的in語法來取得全部需要查詢的信息。

ibatis中循環的使用:

    <select id="SELECT_PROFILE_BY_UIDS" parameterClass="java.util.List" resultMap="userProfileResult">
            SELECT * FROM tb_user_profile WHERE user_id in 
            <iterate open="(" close=")" conjunction=",">#userIds[]#</iterate>
    </select>

 用sql語法能解決一些問題。

看看單個插入:

    public Long addAlbum(AlbumDO album) {
        return (Long) this.getSqlMapClientTemplate().insert("INSERT_ALBUM",
                album);
    }

sqlmap:

    <insert id="INSERT_ALBUM" parameterClass="album">
        insert into tb_user_album (user_id, url, status, create_time,
        edit_time)
        values (#userId#, #url#, #status#, now(), now())
        <selectKey resultClass="java.lang.Long" keyProperty="id">
            SELECT LAST_INSERT_ID() as id
        </selectKey>
    </insert>

改成批量插入:利用sping,注意傳入參數需要final修飾

    public void batchAddAlbum(final List<AlbumDO> list) {if (list != null) {
            this.getSqlMapClientTemplate().execute(new SqlMapClientCallback() {
                @Override
                public Object doInSqlMapClient(SqlMapExecutor executor)
                        throws SQLException {
                    executor.startBatch();
                    for (int i = 0, n = list.size(); i < n; i++) {
                        executor.insert("INSERT_ALBUM", list.get(i));
                    }
                    executor.executeBatch();
                    return null;
                }

            });
        }
    }

建議可以看一下org.springframework.orm.ibatis.SqlMapClientTemplate源碼,更容易理解。

 

4,關於spring手動控制事務

雖然推薦時配置控制事務,但有時候總有呢么幾個變態需求或場景需要手動控制事務,想利用spring。

代碼模型如下:

// 開啟事務
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def); // 獲得事務狀態
        boolean rollbackFlg = true;
        try {
            //數據庫處理1
             //數據庫處理2

                transactionManager.commit(status);//提交
                rollbackFlg = false;//標記位
            }
        } catch (Exception e) {
            logger.error("insert table error : " + e);
            // 回滾事務
            transactionManager.rollback(status);
            rollbackFlg = true;

        } finally {

        }
        // 數據被回滾
        if (rollbackFlg) {
            
        } else {
                 
                }

 

 

 

-----------------------------------------------20140302補充---------------------------

 

項目開發中在很多地方可能會遇到同時插入多條記錄到數據庫的業務場景,如果業務級別循環單條插入數據會不斷建立連接且有多個事務,這個時候如果業務 的事務執行頻率相當較高的話(高並發),對數據庫的性能影響是比較大的;為了提高效率,批量操作會是不錯的選擇,一次批量操作只需要建立一次連接且一個事 務,能很大程度上提高數據庫的效率。

      批量插入操作的sql語句原型如下:

  1. insert  into    
  2.     wsjiang_test(col1, col2, col3)   
  3. values    
  4.     (col1_v, col2_v, col3_v),   
  5.     (col1_v, col2_v, col3_v),  
  6.      ... 

      這里我們以ibatis為例,進行應用說明!

 

     一、 ibatis iterate標簽配置說明

  1. < iterate   
  2.     property =""  /*可選,   
  3.         從傳入的參數集合中使用屬性名去獲取值,   
  4.         這個必須是一個List類型,   
  5.         否則會出現OutofRangeException,   
  6.         通常是參數使用java.util.Map時才使用,   
  7.         如果傳入的參數本身是一個java.util.List, 不能只用這個屬性.  
  8.         不知道為啥官網: http://ibatis.apache.org/docs/dotnet/datamapper/ch03s09.html#id386679  
  9.         說這個屬性是必須的, 但是測試的時候是可以不設置這個屬性的, 還望那位大蝦知道, 講解一下.  
  10.         */  
  11.     conjunction =""  /*可選,   
  12.         iterate可以看作是一個循環,   
  13.         這個屬性指定每一次循環結束后添加的符號,   
  14.          比如使每次循環是OR的, 則設置這個屬性為OR*/  
  15.     open =""  /*可選, 循環的開始符號*/  
  16.     close =""  /*可選, 循環的結束符號*/  
  17.     prepend =""  /*可選, 加在open指定的符號之前的符號*/  
  18. > </ iterate >

 

       二、 ibatis iterate標簽使用示例

              1、批量查詢

  1. < select  id ="iterate_query"  parameterClass ="java.util.List" >   
  2.     <![CDATA[  
  3.         selelct * from wsjiang_test where id=1
  4.     ]]>   
  5.     < iterate  prepend ="prepend"  conjunction ="conn"  open ="open"  colse ="close" >   
  6.         /*使用java.util.List作為參數不能設置property屬性*/  
  7.         <![CDATA[  
  8.             #v[]#  
  9.         ]]> /*這里的"[]"是必須的, 要不然ibatis會把v直接解析為一個String*/  
  10.     </ iterate >   
  11. </ select >

             如果傳入一個List為[123,234,345], 上面的配置將得到一個sql語句:

                   select * from wsjiang_test where id=1 prepend open 123 conn 234 conn 345 close

 

            2、批量插入

               A、不使用open/close屬性

  1. < insert  id =" iterate_insert1 "  parameterClass ="java.util.List" >   
  2.     <![CDATA[  
  3.         insert into wsjinag_test( col1 , col2 , col3 ) values  
  4.     ]]>    
  5.     < iterate  conjunction ="," >   
  6.         <![CDATA[  
  7.             (#test[]. col1 #, # test []. col2 #, # test []. col3 #)  
  8.         ]]>   
  9.     </ iterate >   
  10. </ insert >

              上面的配置將得到一個sql語句:

                   insert  into wsjiang_test( col1, col2, col3values  (?, ?, ?) , (?, ?, ?) , (?, ?, ?) 

 

              B、使用open/close屬性

  1. < insert  id ="betchAddNewActiveCode"  parameterClass ="java.util.List" >   
  2.    <![CDATA[  
  3.         insert into wsjinag_test( col1 , col2 , col3 ) values  
  4.     ]]>
  5.     < iterate  conjunction =","  open ="("  close =")" >   
  6.         <![CDATA[  
  7.             /*這里不加"("和")"*/  
  8.             #test[]. col1 #, # test []. col2 #, # test []. col3 #
  9.         ]]>   
  10.     </ iterate >   
  11. </ insert >

             上面的配置將得到一個sql語句:

                  insert  into wsjiang_test( col1, col2, col3values  (?, ?, ?    ,     ?, ?, ?     ,     ?, ?, ?)

 

         這兩種使用方式區別是相當大的. conjunction, open 和close這幾個屬性需小心使用,將其區分開.

 

    三、單條插入返回新增記錄主鍵

          通常情況,ibatis的insert方法需要返回新增記錄的主鍵,但並非任何表的insert操作都會返回主鍵(這是一個陷阱);要返回這個新增記錄的主鍵,前提是表的主鍵是自增型的,或者是Sequence的;且必須啟用ibatis的selectKey 標簽; 否則獲取新增記錄主鍵的值為0或者null。

         ibatis的配置:

  1. < insert  id =" iterate_insert1 "  parameterClass ="Object" >   
  2.     <![CDATA[  
  3.         insert into wsjinag_test( col1 , col2 , col3 )
  4.         values   (# col1 #, # col2 #, # col3 #)  
  5.     ]]>    
  6.     < selectKey   keyProperty ="id" resultClass= "Long" >   
  7.         <![CDATA[  
  8.             SELECT LAST_INSERT_ID() AS value  
  9.         ]]>   
  10.     </ selectKey >   
  11. </ insert >

 

  四、 插入返回 新增記錄數

      在第三節中已經講清楚通過ibatis的insert方法只能得到新增記錄的ID; 如果對於無需知道新增記錄ID,只需要知道有沒有插入成功的業務場景時,特別是對於批量插入,配置的selectKey 可能會有問題時,一次插入多條,拿不到新增的ID,這時我們就只能返回插入成功的記錄數來區分是否新增成功!但是insert方法是不會返回記錄數;於是我們可以使用ibatis的update方法來調用沒有配置 selectKey 標簽的insert語句,這樣就能返回影響(插入)的記錄數了!

 

     某些地方理解不是很深刻,還請不吝賜教!

 

讓我們繼續前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不會成功。
共勉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM