拓展mybatisPlus实现类方法,批量插入时,唯一索引冲突后更新

拓展mybatisPlus实现类方法,批量插入时,唯一索引冲突后更新

唯一索引注解

package com.zk.fahai.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author ZHAIKE
 * @date 2022/01/04 9:28
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FhId {}

拓展服务类,使用时extend即可

package com.zk.fahai.util;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zk.fahai.common.annotation.FhId;
import com.zk.fx.common.util.JsonUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;

  /** 批量插入,冲突更新 @param entityList 数据 */
  @Transactional(rollbackFor = Exception.class)
  public void saveOrUpdateBatchByFhId(Collection<T> entityList) throws IOException {
    TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
    Assert.notNull(
        tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");

    List<String> ukFieldNameList =
        tableInfo.getFieldList().stream()
            .filter(c -> c.getField().isAnnotationPresent(FhId.class))
            .map(tableFieldInfo -> tableFieldInfo.getField().getName())
            .collect(Collectors.toList());
    if (ukFieldNameList.size() < 1) {
      logger.error("未找到唯一索引,class:{}", tableInfo.getEntityType());
      return;
    }
    for (List<T> list : this.cutList(entityList, IService.DEFAULT_BATCH_SIZE)) {
      try {
        super.saveBatch(list);
      } catch (DuplicateKeyException duplicateKeyException) {
        for (T entity : list) {
          try {
            super.save(entity);
          } catch (DuplicateKeyException e2) {
            logger.info("数据更新触发唯一索引,对象:{}", JsonUtils.object2JsonString(entity));
            UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
            ukFieldNameList.forEach(
                uk -> updateWrapper.eq(uk, ReflectionKit.getFieldValue(entity, uk)));
            super.update(entity, updateWrapper);
          }
        }
      }
    }
  }

  /** 切分集合 */
  public <C> List<List<C>> cutList(Collection<C> list, int maxNum) {
    int step = (list.size() + maxNum - 1) / maxNum;
    return Stream.iterate(0, n -> n + 1)
        .limit(step)
        .parallel()
        .map(
            a ->
                list.stream()
                    .skip(a * maxNum)
                    .limit(maxNum)
                    .parallel()
                    .collect(Collectors.toList()))
        .collect(Collectors.toList());
  }
}

不使用saveBatch版,拓展服务类,使用时extend即可

发现oracle使用savebatch时有问题

package com.zk.fahai.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 法海数据唯一索引
 *
 * @author ZHAIKE
 * @date 2022/01/04 9:28
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FhUniqueIndex {}

package com.zk.fahai.service.fh;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zk.fahai.common.annotation.FhUniqueIndex;
import com.zk.fx.common.util.JsonUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;

/**
 * 拓展 mybatis-plus方法,数据落库使用
 *
 * @author ZHAIKE
 * @date 2022/01/17 16:15
 */
public class DataStorageExpandService<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {

  private static final Logger logger = LoggerFactory.getLogger(DataStorageExpandService.class);

  /** 插入,冲突更新 @param entityList 数据 */
  @Transactional(rollbackFor = Exception.class)
  public void saveOrUpdateByFhUk(List<T> entityList) throws IOException {
    List<String> fhUkFieldNameList = this.getFhUkFieldNameList();
    for (T entity : entityList) {
      this.saveOrUpdateByFhUniqueIndex(fhUkFieldNameList, entity);
    }
  }

  /** 插入,冲突更新 @param entity 数据 */
  @Transactional(rollbackFor = Exception.class)
  public void saveOrUpdateByFhUk(T entity) throws IOException {
    this.saveOrUpdateByFhUniqueIndex(this.getFhUkFieldNameList(), entity);
  }

  private void saveOrUpdateByFhUniqueIndex(List<String> fhUkFieldNameList, T entity)
      throws IOException {
    try {
      super.save(entity);
    } catch (DuplicateKeyException e2) {
      QueryWrapper<T> queryWrapper = new QueryWrapper<>();
      fhUkFieldNameList.forEach(
          ukFieldName -> {
            Object ukFieldValue = ReflectionKit.getFieldValue(entity, ukFieldName);
            queryWrapper.eq(ukFieldName, ukFieldValue);
          });
      this.fillId(entity, queryWrapper);
      logger.info("数据保存触发唯一索引,进行更新,数据 ->{}", JsonUtils.object2JsonString(entity));
      super.updateById(entity);
    }
  }

  /** 获取主键回填 */
  private void fillId(T entity, QueryWrapper<T> queryWrapper) {
    TableInfo tableInfo = TableInfoHelper.getTableInfo(super.entityClass);
    Assert.notNull(
        tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
    String keyProperty = tableInfo.getKeyProperty();
    Assert.notEmpty(
        keyProperty, "error: can not execute. because can not find column for id from entity!");
    Long idVal = (Long) ReflectionKit.getFieldValue(super.getOne(queryWrapper), keyProperty);
    try {
      Field idField = entity.getClass().getDeclaredField(keyProperty);
      idField.setAccessible(true);
      idField.set(entity, idVal);
    } catch (NoSuchFieldException | IllegalAccessException e) {
      logger.error("获取主键回填异常", e);
    }
  }

  private List<String> getFhUkFieldNameList() {
    TableInfo tableInfo = TableInfoHelper.getTableInfo(super.entityClass);
    Assert.notNull(
        tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
    List<TableFieldInfo> tableFieldInfoList =
        tableInfo.getFieldList().stream()
            .filter(c -> c.getField().isAnnotationPresent(FhUniqueIndex.class))
            .collect(Collectors.toList());
    Assert.notEmpty(
        tableFieldInfoList,
        "error: can not execute. because can not find @FhUniqueIndex for entity!");
    List<String> fhUkFieldNameList =
        tableFieldInfoList.stream()
            .map(tableFieldInfo -> tableFieldInfo.getField().getName())
            .collect(Collectors.toList());
    Assert.notEmpty(
        fhUkFieldNameList,
        "error: can not execute. because can not find column for @FhUniqueIndex from entity!");
    return fhUkFieldNameList;
  }
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZHAIKEsir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值