关于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语句,这样就能返回影响(插入)的记录数了!

     某些地方理解不是很深刻,还请不吝赐教!

让我们继续前行

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

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

原文地址:https://www.cnblogs.com/killbug/p/3056450.html