MyBatis foreach 批量更新实例

在做配置选项(设备类型,所属楼层等)的时候,当删除某配置的时候,我需要检验该配置是否已被删除。

@Override
 public BaseVO deleteOptionDetail(Integer id) {
 
  // 合法性验证
  if (null == id) {
   return ParamErrorVO.getInstance();
  }
  ConfigOptionDetail configOptionDetail = configOptionDetailMapper.selectById(id);
  if (null == configOptionDetail || 1 == configOptionDetail.getIsDeleted()) {
   return new ErrorVO("该配置不存在");
  }
  if (configOptionDetail.getSystem() == 1) {
   return new ErrorVO("系统属性不能删除");
  }
  if (configOptionDetail.getUseCount() = 0) {
   return new ErrorVO("配置正在使用不能删除,请先删除使用配置的地方");
  }
 
  // 合法性通过
  configOptionDetail.setIsDeleted(1);
  configOptionDetail.setGmtModefied(Calendar.getInstance().getTime());
  configOptionDetailMapper.updateById(configOptionDetail);
 
  // 更新内存配置
  ConfigOptionConstruct.updateOption();
  return SuccessVO.getInstance();
 }

思考之后我决定采用,给配置选项设备一个use_count字段,代表该配置被引用的次数。 只有当该字段值为 0 时,该配置选项记录才可被删除。

使用情况:

我需要批量删除房间, 删除房间的同时,room对象中使用到了所属楼层的配置选项,我需要将他们的引用减少

@Override
 public BaseVO deleteRoomByIds(Integer[] ids) {
  if (null == ids) {
   return ParamErrorVO.getInstance();
  }
  EntityWrapper<Room> entityWrapper = new EntityWrapper<>();
  entityWrapper.where("isdelete={0}", 0);
  // 核查删除的房间中是否存在正在使用的设备
  List<Integer> notDelete = deviceInfoService.checkRoomIds(ids);
  if (null != notDelete && 0 != notDelete.size()) {
   // 存在仍在使用设备的房间
   entityWrapper.in("id", notDelete);
   // 查询这些房间
   List<Room> roomList = roomMapper.selectList(entityWrapper);
   // 获取房间的名称
   StringBuilder stringBuilder = new StringBuilder(roomList.stream().map(Room::getName).collect(Collectors.toList()).toString());
   System.out.println(stringBuilder);
   // TODO: 2018/4/8 可能需要修改提示语
   return new ErrorVO(stringBuilder + " 房间存在未删除的设备,请先删除设备");
  }
 
  // 房间没有设备在使用
  List<Integer> idList = new ArrayList<>();
  idList.addAll(Arrays.asList(ids));
  // 查询需要删除的房间
  entityWrapper.in("id", idList);
  List<Room> roomList = roomMapper.selectList(entityWrapper);
  if (null == roomList || idList.size() != roomList.size()) {
   return new ErrorVO("存在错误的房间");
  }
  // ******************************************************************************************** 重点
  // 可以逻辑删除
  int count = roomMapper.logicDeleteByIds(idList);
  List<Long> optionIds = roomList.stream().map(room -> Long.parseLong(room.getRoomPosition())).collect(Collectors.toList());
  Map<Long, Long> optionIdsMap = optionIds.stream().collect(Collectors.groupingBy(p -> p,Collectors.counting()));
  // 移除所属楼层配置选项的使用
  configOptionDetailService.removeUseCount(optionIdsMap);
  ConfigOptionConstruct.updateOption();
  if (count == idList.size()) {
   return SuccessVO.getInstance();
  } else {
   return new ErrorVO("部分删除失败");
  } 
 }

optionIds 是从roomList 房间集合中,通过stream, 所引用的配置选项id集合

上面我红字标明他们,是因为,如果房间A 是一楼, 房间B 也是一楼, 那么我应该将一楼的引用减 2。

所以我将optionIds 分组转成Map<配置选项id, 需要减少引用的次数>

最后一步,也是最重要的进行数据库操作,我希望可以批量更新减少这些引用。

查看MyBatis文档:

foreach

动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
 SELECT *
 FROM POST P
 WHERE ID in
 <foreach item="item" index="index" collection="list"
  open="(" separator="," close=")">
  #{item}
 </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

注意 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

<update id="addUseCountByIds">
 update config_option_detail
 set gmt_modified = #{gmtModified}, use_count = use_count +
 <foreach item="item" index="index" collection="list" open=" case id " separator=" " close=" end">
 when #{index} then #{item}
 </foreach>
 where id in
 <foreach item="item" index="index" collection="list"
  open="(" separator="," close=")">
 #{index}
 </foreach>
</update>

补充:mybatis 用<foreach>根据ID批量更新时的一个注意点。

看接口。传入一个Long型的List。

int updateReadCount(@Param(value = "topicIdList") List<Long> topicIdList);

xml里面循环update.

<update id="updateReadCount" parameterType="java.util.List">
  update CTS
  set read_count = read_count + 1
  where topic_id in
  <foreach item="item" index="index" collection="topicIdList" open="(" close=")" separator=",">
   #{item.topicId}
  </foreach>
 </update>

就是直接复制了别人的代码,改了一改。怎么都跑不通。。。。。。。

问题就出在这个item,item 表示集合中每一个元素进行迭代时的别名。

List<Long> topicIdList 因为时Long型(不是entity封装着的),就不需要别名了。改为如下就可以跑通了。

<update id="updateReadCount" parameterType="java.util.List">
  update CTS
  set read_count = read_count + 1
  where topic_id in
  <foreach item="topicId" index="index" collection="topicIdList" open="(" close=")" separator=",">
   #{topicId}
  </foreach>
 </update>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持云海天教程。如有错误或未考虑完全的地方,望不吝赐教。