content: package com.admin4j.framework.mp.service.impl;
  
  import com.admin4j.framework.mp.service.IBizService;
  import com.admin4j.framework.mp.service.ICommandBatchService;
  import com.admin4j.framework.mp.service.ICommandService;
  import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
  import com.baomidou.mybatisplus.core.enums.SqlMethod;
  import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  import com.baomidou.mybatisplus.core.metadata.TableInfo;
  import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
  import com.baomidou.mybatisplus.core.toolkit.*;
  import com.baomidou.mybatisplus.extension.service.IService;
  import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
  import org.apache.ibatis.binding.MapperMethod;
  import org.apache.ibatis.logging.Log;
  import org.apache.ibatis.logging.LogFactory;
  import org.apache.ibatis.session.SqlSession;
  import org.springframework.transaction.annotation.Transactional;
  
  import java.io.Serializable;
  import java.util.Collection;
  import java.util.Objects;
  import java.util.function.BiConsumer;
  
  /**
   * @author andanyang
   * @since 2023/8/17 11:04
   */
  
  public class CommandServiceImpl<M extends BaseMapper<T>, T> extends QueryServiceImpl<M, T> implements ICommandService<T>, ICommandBatchService<T>, IBizService<T> {
  
      protected final static int DEFAULT_BATCH_SIZE = 1000;
      protected Log log = LogFactory.getLog(getClass());
  
  
      @Override
      public M getBaseMapper() {
          return baseMapper;
      }
  
      /**
       * 普通查询
       *
       * @return UpdateWrapper 的包装类
       */
      protected UpdateWrapper<T> update() {
          return Wrappers.update();
      }
  
      /**
       * 普通查询
       *
       * @return UpdateWrapper 的包装类
       */
      protected UpdateWrapper<T> update(T entity) {
          return Wrappers.update(entity);
      }
  
      /**
       * 链式查询 lambda 式
       * <p>注意：不支持 Kotlin </p>
       *
       * @return LambdaUpdateWrapper 的包装类
       */
      protected LambdaUpdateWrapper<T> lambdaUpdate() {
          return Wrappers.lambdaUpdate();
      }
  
      /**
       * 链式查询 lambda 式
       * <p>注意：不支持 Kotlin </p>
       *
       * @param entity 实体对象
       * @return LambdaUpdateWrapper 的包装类
       */
      protected LambdaUpdateWrapper<T> lambdaUpdate(T entity) {
          return Wrappers.lambdaUpdate(entity);
      }
  
      /**
       * 插入一条记录（选择字段，策略插入）
       *
       * @param entity 实体对象
       */
      @Override
      public boolean save(T entity) {
          return SqlHelper.retBool(getBaseMapper().insert(entity));
      }
  
      /**
       * 插入（批量）
       *
       * @param entityList 实体对象集合
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean saveBatch(Collection<T> entityList) {
          return saveBatch(entityList, DEFAULT_BATCH_SIZE);
      }
  
  
      /**
       * 批量插入
       *
       * @param entityList ignore
       * @param batchSize  ignore
       * @return ignore
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean saveBatch(Collection<T> entityList, int batchSize) {
          String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
          return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
      }
  
      /**
       * 获取mapperStatementId
       *
       * @param sqlMethod 方法名
       * @return 命名id
       * @since 3.4.0
       */
      protected String getSqlStatement(SqlMethod sqlMethod) {
          return SqlHelper.getSqlStatement(mapperClass, sqlMethod);
      }
  
      /**
       * TableId 注解存在更新记录，否插入一条记录
       *
       * @param entity 实体对象
       * @return boolean
       */
      @Transactional(rollbackFor = Exception.class)
      @Override
      public boolean saveOrUpdate(T entity) {
          if (null != entity) {
              TableInfo tableInfo = TableInfoHelper.getTableInfo(this.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!");
              Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
              return StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal)) ? save(entity) : updateById(entity);
          }
          return false;
      }
  
  
      /**
       * 批量修改插入
       *
       * @param entityList 实体对象集合
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean saveOrUpdateBatch(Collection<T> entityList) {
          return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
      }
  
      /**
       * 批量修改插入
       * Params:
       *
       * @param entityList 实体对象集合
       * @param batchSize  每次的数量
       * @return
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
          TableInfo tableInfo = TableInfoHelper.getTableInfo(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!");
          return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {
              Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
              return StringUtils.checkValNull(idVal)
                      || CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
          }, (sqlSession, entity) -> {
              MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
              param.put(Constants.ENTITY, entity);
              sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
          });
      }
  
      /**
       * 根据 ID 选择修改
       *
       * @param entity 实体对象
       */
      @Override
      public boolean updateById(T entity) {
          return SqlHelper.retBool(getBaseMapper().updateById(entity));
      }
  
      /**
       * 根据ID 批量更新
       *
       * @param entityList 实体对象集合
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean updateBatchById(Collection<T> entityList) {
          return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
      }
  
      /**
       * 根据ID 批量更新
       *
       * @param entityList 实体对象集合
       * @param batchSize  更新批次数量
       * @return
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean updateBatchById(Collection<T> entityList, int batchSize) {
          String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);
          return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
              MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
              param.put(Constants.ENTITY, entity);
              sqlSession.update(sqlStatement, param);
          });
      }
  
      /**
       * 执行批量操作
       *
       * @param list      数据集合
       * @param batchSize 批量大小
       * @param consumer  执行方法
       * @param <E>       泛型
       * @return 操作结果
       * @since 3.3.1
       */
      protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
          return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);
      }
  
      /**
       * 执行批量操作（默认批次提交数量{@link IService#DEFAULT_BATCH_SIZE}）
       *
       * @param list     数据集合
       * @param consumer 执行方法
       * @param <E>      泛型
       * @return 操作结果
       * @since 3.3.1
       */
      protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {
          return executeBatch(list, DEFAULT_BATCH_SIZE, consumer);
      }
  
      @Override
      public boolean removeById(Serializable id) {
          TableInfo tableInfo = TableInfoHelper.getTableInfo(getEntityClass());
          if (tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) {
              return removeById(id, true);
          }
          return SqlHelper.retBool(getBaseMapper().deleteById(id));
      }
  
      /**
       * 根据实体(ID)删除
       *
       * @param entity 实体
       * @since 3.4.4
       */
      protected boolean removeById(T entity) {
          return SqlHelper.retBool(getBaseMapper().deleteById(entity));
      }
  
      @Override
      public boolean removeById(Serializable id, boolean useFill) {
          TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
          if (useFill && tableInfo.isWithLogicDelete()) {
              if (!entityClass.isAssignableFrom(id.getClass())) {
                  T instance = tableInfo.newInstance();
                  tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), id);
                  return removeById(instance);
              }
          }
          return SqlHelper.retBool(getBaseMapper().deleteById(id));
      }
  
  
      /**
       * 删除（根据ID 批量删除）
       *
       * @param list 主键ID或实体列表
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean removeByIds(Collection<?> list) {
          if (CollectionUtils.isEmpty(list)) {
              return false;
          }
          TableInfo tableInfo = TableInfoHelper.getTableInfo(getEntityClass());
          if (tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) {
              return removeBatchByIds(list, true);
          }
          return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
      }
  
      /**
       * 批量删除(jdbc批量提交)
       *
       * @param list    主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)
       * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除)
       * @return 删除结果
       * @since 3.5.0
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean removeBatchByIds(Collection<?> list, boolean useFill) {
          return removeBatchByIds(list, DEFAULT_BATCH_SIZE, useFill);
      }
  
      /**
       * 批量删除
       *
       * @param list    主键ID或实体列表
       * @param useFill 是否填充(为true的情况,会将入参转换实体进行delete删除)
       * @return 删除结果
       * @since 3.5.0
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean removeByIds(Collection<?> list, boolean useFill) {
          if (CollectionUtils.isEmpty(list)) {
              return false;
          }
          if (useFill) {
              return removeBatchByIds(list, true);
          }
          return SqlHelper.retBool(getBaseMapper().deleteBatchIds(list));
      }
  
  
      /**
       * 批量删除(jdbc批量提交)
       *
       * @param list 主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)
       * @return 删除结果
       * @since 3.5.0
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean removeBatchByIds(Collection<?> list) {
          return removeBatchByIds(list, DEFAULT_BATCH_SIZE);
      }
  
      /**
       * 批量删除(jdbc批量提交)
       *
       * @param list      主键ID或实体列表
       * @param batchSize 批次大小
       * @return 删除结果
       * @since 3.5.0
       */
      @Transactional(rollbackFor = Exception.class)
      public boolean removeBatchByIds(Collection<?> list, int batchSize) {
          TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
          return removeBatchByIds(list, batchSize, tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill());
      }
  
  
      @Transactional(rollbackFor = Exception.class)
      public boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
          String sqlStatement = getSqlStatement(SqlMethod.DELETE_BY_ID);
          TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
          return executeBatch(list, batchSize, (sqlSession, e) -> {
              if (useFill && tableInfo.isWithLogicDelete()) {
                  if (entityClass.isAssignableFrom(e.getClass())) {
                      sqlSession.update(sqlStatement, e);
                  } else {
                      T instance = tableInfo.newInstance();
                      tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), e);
                      sqlSession.update(sqlStatement, instance);
                  }
              } else {
                  sqlSession.update(sqlStatement, e);
              }
          });
      }
  
  }
  