基于 MybatisPlus 邏輯刪除開啟情況下兼容多數(shù)據(jù)庫實現(xiàn)批量插入更新通用流程實現(xiàn)

基于 MybatisPlus 邏輯刪除開啟情況下兼容多數(shù)據(jù)庫實現(xiàn)批量插入更新通用流程實現(xiàn)

背景

  • 項目上業(yè)務(wù)流程有大量使用 mysql 批量插入更新語法 insert on duplicate update
  • 由于現(xiàn)在公司業(yè)務(wù)需要匾效,同時兼容達(dá)夢數(shù)據(jù)庫使用盒卸,達(dá)夢數(shù)據(jù)庫雖然也有類似的語法菠隆,不過使用起來也比較麻煩镜硕,生成相應(yīng)的 SQL 可以看我這一篇文章

核心問題

  1. MySQL 更新插入流程如何使用實現(xiàn)锚烦?
  2. 如何實現(xiàn)多個數(shù)據(jù)庫兼容插入更新的流程?
  3. 如何編寫工具類優(yōu)雅實現(xiàn)插入通用流程封裝以及整合 MybatisPlus lambda 表達(dá)式查詢,達(dá)到方便易用的效果?
  4. 開啟 MybatisPlus 邏輯刪除功能连锯,怎么通過自定義 SQL 查詢出所有數(shù)據(jù)(插入更新流程可能涉及到所有的數(shù)據(jù)归苍,并不是只是處理未邏輯刪除的數(shù)據(jù))?
  5. 批量數(shù)據(jù)插入更新速度如何優(yōu)化运怖?

代碼實現(xiàn)

實現(xiàn)多數(shù)據(jù)庫兼容插入更新操作拼弃,只能根據(jù) MySQL 插入更新原理利用代碼抽象通用化流程,較好的通用化方式是使用 MybatisPlus 自帶的通用 CRUD 邏輯方法實現(xiàn)摇展,但是原有自帶方法開啟邏輯刪除功能以后吻氧,查詢方法都會自帶過濾邏輯刪除的數(shù)據(jù),需要實現(xiàn)自定義 SQL 注入器咏连,為了實現(xiàn)一套不帶邏輯刪除的通用方法

拓展自定義 SQL 注入器實現(xiàn)

新增自定義方法

SelectList 方法不帶邏輯刪除

public class SelectListWithoutLogicDelete extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql = String.format(
                SqlMethod.SELECT_LIST.getSql(),
                sqlFirst(),
                sqlSelectColumns(tableInfo, true),
                tableInfo.getTableName(),
                sqlWhereEntityWrapper(true, tableInfo),
                sqlComment()
        );

        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);

        String mapperMethodName = StrUtil.lowerFirst(getClass().getSimpleName());
        return addSelectMappedStatementForTable(mapperClass, mapperMethodName, sqlSource, tableInfo);
    }

    // 重寫 sqlWhereEntityWrapper 盯孙,去掉邏輯刪除相關(guān)代碼
    @Override
    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
        String sqlScript = table.getAllSqlWhere(false, true, WRAPPER_ENTITY_DOT);
        sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY), true);
        sqlScript += NEWLINE;
        sqlScript += SqlScriptUtils.convertIf(String.format(SqlScriptUtils.convertIf(" AND", String.format("%s and %s", WRAPPER_NONEMPTYOFENTITY, WRAPPER_NONEMPTYOFNORMAL), false) + " ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                        WRAPPER_NONEMPTYOFWHERE), true);
        sqlScript = SqlScriptUtils.convertWhere(sqlScript) + NEWLINE;
        sqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                        WRAPPER_EMPTYOFWHERE), true);
        sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER), true);
        return newLine ? NEWLINE + sqlScript : sqlScript;
    }
}

UpdateById 方法不帶邏輯刪除

public class UpdateByIdWithoutLogicDelete extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;

        final String additional = optlockVersion(tableInfo);
        String sql = String.format(
                sqlMethod.getSql(),
                tableInfo.getTableName(),
                // 搬運(yùn) UpdateById 代碼,第一個參數(shù)變化了
                sqlSet(false, false, tableInfo, false, ENTITY, ENTITY_DOT),
                tableInfo.getKeyColumn(),
                ENTITY_DOT + tableInfo.getKeyProperty(),
                additional
        );

        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        String mapperMethodName = StrUtil.lowerFirst(getClass().getSimpleName());
        return addUpdateMappedStatement(mapperClass, modelClass, mapperMethodName, sqlSource);
    }
}

注入不帶邏輯刪除自定義 mapper 方法

public class SqlInjectorExtension extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methods = super.getMethodList(mapperClass);
        // 原來基礎(chǔ)上注入兩個新方法
        methods.add(new SelectListWithoutLogicDelete());
        methods.add(new UpdateByIdWithoutLogicDelete());
        return methods;
    }
}

封裝自定義 mapper

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import org.apache.ibatis.annotations.Param;

public interface BaseMapperExtension<T> extends BaseMapper<T> {
    // === ChainWrappers 工具類轉(zhuǎn)發(fā) ===

    /**
     * 鏈?zhǔn)讲樵?普通
     *
     * @return QueryWrapper 的包裝類
     */
    default QueryChainWrapper<T> queryChain() {
        return new QueryChainWrapper<>(this);
    }

    /**
     * 鏈?zhǔn)讲樵?lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaQueryWrapper 的包裝類
     */
    default LambdaQueryChainWrapper<T> lambdaQueryChain() {
        return new LambdaQueryChainWrapper<>(this);
    }

    /**
     * 鏈?zhǔn)礁?普通
     *
     * @return UpdateWrapper 的包裝類
     */
    default UpdateChainWrapper<T> updateChain() {
        return new UpdateChainWrapper<>(this);
    }

    /**
     * 鏈?zhǔn)礁?lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaUpdateWrapper 的包裝類
     */
    default LambdaUpdateChainWrapper<T> lambdaUpdateChain() {
        return new LambdaUpdateChainWrapper<>(this);
    }

    // === 自定義 sql ===

    /**
     * 忽略邏輯刪除功能的 selectList 方法
     *
     * @param queryWrapper
     *         查詢條件
     *
     * @return {@code List<T>}
     */
    List<T> selectListWithoutLogicDelete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 忽略邏輯刪除功能的 updateById 方法
     *
     * @param entity
     *         實體
     *
     * @return int
     */
    int updateByIdWithoutLogicDelete(@Param(Constants.ENTITY) T entity);
}

封裝自定義 IServiceExtensionServiceImplExtension 模板

public interface IServiceExtension<T extends IBaseDO<T>> extends IService<T> {
    @Override
    BaseMapperExtension<T> getBaseMapper();

    // === BaseMapperExtension 新默認(rèn)方法 ===

    /**
     * 通過 id 更新實體(忽略邏輯刪除功能)
     *
     * @param entity
     *         實體數(shù)據(jù)
     *
     * @return boolean
     */
    default boolean updateByIdWithoutLogicDelete(T entity) {
        return SqlHelper.retBool(getBaseMapper().updateByIdWithoutLogicDelete(entity));
    }

    default List<T> listWithoutLogicDelete(Wrapper<T> queryWrapper) {
        return getBaseMapper().selectListWithoutLogicDelete(queryWrapper);
    }

    default List<T> listWithoutLogicDelete() {
        return listWithoutLogicDelete(Wrappers.emptyWrapper());
    }

    // === 增強(qiáng)型方法 ===

    /**
     * 批量根據(jù) ID 更新(忽略邏輯刪除)
     *
     * @param entityList
     *         實體列表
     * @param batchSize
     *         批次大小
     *
     * @return boolean
     */
    boolean updateBatchByIdWithoutLogicDelete(Collection<T> entityList, int batchSize);
}
public abstract class ServiceImplExtension<M extends BaseMapperExtension<T>, T extends IBaseDO<T>> extends ServiceImpl<M, T> implements IServiceExtension<T> {}

新增 MybatisPlusUtils

封裝 lambda 表達(dá)式通用插入更新流程

  • 基礎(chǔ)實體類接口 IBaseDO
import java.util.Date;

public interface IBaseDO<E> {
     Long getId();

     E setId(Long id);

     Date getCreateDate();

     E setCreateDate(Date createDate);

     Date getUpdateDate();

     E setUpdateDate(Date updateDate);
}
  • 抽象實體類
@EqualsAndHashCode
public abstract class BaseDO<E extends BaseDO<E>> implements IBaseDO<E> {
    @ApiModelProperty(value = "主鍵")
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;
    
    @ApiModelProperty(value = "創(chuàng)建時間")
    @TableField(value = "create_date", fill = FieldFill.INSERT)
    private Date createDate;
    
    @ApiModelProperty(value = "更新時間")
    @TableField(value = "update_date", fill = FieldFill.INSERT_UPDATE)
    private Date updateDate;

    @Override
    public Long getId() {
        return id;
    }

    @Override
    public E setId(Long id) {
        this.id = id;
        return self();
    }

    private E self() {
        return (E) this;
    }

    @Override
    public Date getCreateDate() {
        return createDate;
    }

    @Override
    public E setCreateDate(Date createDate) {
        this.createDate = createDate;
        return self();
    }

    @Override
    public Date getUpdateDate() {
        return updateDate;
    }

    @Override
    public E setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
        return self();
    }

    public E fillLatestDate() {
        Date date = new Date();
        createDate = date;
        updateDate = date;
        return self();
    }

    public E fillDate(Date date) {
        createDate = date;
        updateDate = date;
        return self();
    }
}
  • MyBatisPlusUtils 工具類
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.util.*;
import java.util.function.BiFunction;

import static java.util.stream.Collectors.*;

@Slf4j
public abstract class MyBatisPlusUtils {
    /**
     * mysql insert on duplicate key update 替代方法
     *
     * @param service
     *         service 類
     * @param dataCollect
     *         新數(shù)據(jù)列表
     * @param isFillId
     *         無 ID 是否填充 ID
     * @param uniqueCondition
     *         唯一索引
     * @param updateMapping
     *         更新 mapping:函數(shù)入?yún)椴樵兊降呐f數(shù)據(jù) & 新數(shù)據(jù)祟滴,出參為更新后的舊數(shù)據(jù)(不能設(shè)置id和相關(guān)唯一索引)
     * @param <T>
     *         實體泛型
     */
    public static <T extends IBaseDO<T>, S extends IServiceExtension<T>> List<T> insertOnUpdateBatch(
            S service,
            Collection<T> dataCollect,
            boolean isFillId,
            BiFunction<T, LambdaQueryWrapper<T>, LambdaQueryWrapper<T>> uniqueCondition,
            BiFunction<T, T, T> updateMapping
    ) {
        Objects.requireNonNull(service);
        Objects.requireNonNull(dataCollect);
        Objects.requireNonNull(updateMapping);
        Objects.requireNonNull(uniqueCondition);

        Class<T> entityClass = service.getEntityClass();
        Objects.requireNonNull(entityClass);

        if (CollectionUtils.isEmpty(dataCollect)) {
            log.debug("[{}] insert on update data size: 0,ignore", service.getEntityClass().getSimpleName());
            return new ArrayList<>();
        }

        Map<Boolean, List<T>> collect = dataCollect.stream()
                .filter(Objects::nonNull)
                .peek(newData -> execFillIdStrategy(isFillId, newData))
                .flatMap(newData -> {
                    LambdaQueryWrapper<T> chainWrapper = uniqueCondition.apply(newData, Wrappers.lambdaQuery(service.getEntityClass()));
                    Objects.requireNonNull(chainWrapper);
                    // 構(gòu)造通過 ID 或者 多個唯一索引查詢條件振惰,進(jìn)行數(shù)據(jù)庫查詢,查詢結(jié)果可能查詢出來多條
                    List<Pair<Boolean, T>> updateData = service.listWithoutLogicDelete(chainWrapper.or(wrapper -> wrapper.eq(IBaseDO::getId, newData.getId())))
                            .stream()
                            .map(oldData -> {
                                // 查詢出來需要更新的數(shù)據(jù)
                                Date newUpdateDate = newData.getUpdateDate();
                                Date oldCreateDate = oldData.getCreateDate();
                                Date now = new Date();
                                // 更新操作回調(diào)
                                T update = updateMapping.apply(newData, oldData)
                                        // 指定填充默認(rèn)更新字段
                                        .setCreateDate(oldCreateDate == null ? now : oldCreateDate)
                                        .setUpdateDate(newUpdateDate == null ? now : newUpdateDate);
                                // 轉(zhuǎn)換器返回不同 ID 數(shù)據(jù)拋出異常
                                if (!Objects.equals(update.getId(), oldData.getId())) {
                                    throw new RuntimeException("mapping can't return new data");
                                }
                                return Pair.of(false, update);
                            })
                            .collect(toList());

                    if (CollectionUtils.isNotEmpty(updateData)) {
                        return updateData.stream();
                    }
                    
                    // 如果上面不符合踱启,就是需要插入的新數(shù)據(jù)
                    List<Pair<Boolean, T>> insertData = Collections.singletonList(Pair.of(true, newData));
                    return insertData.stream();
                })
                .collect(groupingBy(Pair::getKey, mapping(Pair::getValue, toList())));

        // 提取需要新插入的數(shù)據(jù)
        List<T> insertData = collect.getOrDefault(true, new ArrayList<>());
        if (CollectionUtils.isNotEmpty(insertData)) {
            service.saveBatch(insertData);
        }

        // 提取需要更新的數(shù)據(jù)
        List<T> updateData = collect.getOrDefault(false, new ArrayList<>());
        if (CollectionUtils.isNotEmpty(updateData)) {
            // 批量更新數(shù)據(jù)
            service.updateBatchByIdWithoutLogicDelete(updateData);
        }

        log.debug(
                "[{}] data size:[{}],insert data size:[{}],update data size:[{}]",
                entityClass.getSimpleName(),
                dataCollect.size(),
                insertData.size(),
                updateData.size()
        );
        
        insertData.addAll(updateData);
        return insertData;
    }

    /**
     * 執(zhí)行填充ID策略
     *
     * @param isFillId
     *         是否填充 ID
     * @param newData
     *         數(shù)據(jù)
     * @param <T>
     *         類型
     */
    private static <T extends IBaseDO<T>> void execFillIdStrategy(boolean isFillId, T newData) {
        Long id = newData.getId();
        if (id != null) {
            return;
        }

        if (!isFillId) {
            throw new RuntimeException("data id can't null,data:" + newData);
        }

        newData.setId(IdWorker.getId());
    }
}

session 批量寫庫優(yōu)化

  • 由于原來的實現(xiàn)只需要一條 SQL 進(jìn)行批量更新报账,現(xiàn)在插入更新邏輯是在業(yè)務(wù)代碼中實現(xiàn)研底,最少是需要 2 條 SQL 埠偿,頻繁發(fā)送 SQL 進(jìn)行讀寫操作耗時非常大,我們需要對插入更新操作的 SQL 進(jìn)行批量輸入執(zhí)行榜晦,使用 BATCH 批處理模式冠蒋,減少重復(fù)預(yù)編譯的次數(shù)。

工具類整合

為了更加方便使用乾胶, MybatisPlusUtils 工具類整合進(jìn)去自定義 IServiceExtensionServiceImplExtension 模板

  • 整合后 IServiceExtension
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;

public interface IServiceExtension<T extends IBaseDO<T>> extends IService<T> {
    @Override
    BaseMapperExtension<T> getBaseMapper();

    // === BaseMapperExtension 新默認(rèn)方法 ===

    /**
     * 通過 id 更新實體(忽略邏輯刪除功能)
     *
     * @param entity
     *         實體數(shù)據(jù)
     *
     * @return boolean
     */
    default boolean updateByIdWithoutLogicDelete(T entity) {
        return SqlHelper.retBool(getBaseMapper().updateByIdWithoutLogicDelete(entity));
    }

    default List<T> listWithoutLogicDelete(Wrapper<T> queryWrapper) {
        return getBaseMapper().selectListWithoutLogicDelete(queryWrapper);
    }

    default List<T> listWithoutLogicDelete() {
        return listWithoutLogicDelete(Wrappers.emptyWrapper());
    }

    // === 增強(qiáng)型方法 ===

    /**
     * 批量根據(jù) ID 更新(忽略邏輯刪除)
     *
     * @param entityList
     *         實體列表
     * @param batchSize
     *         批次大小
     *
     * @return boolean
     */
    boolean updateBatchByIdWithoutLogicDelete(Collection<T> entityList, int batchSize);

    /**
     * 批量根據(jù) ID 更新(忽略邏輯刪除)
     *
     * @param entityList
     *         實體列表
     *
     * @return boolean
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean updateBatchByIdWithoutLogicDelete(Collection<T> entityList) {
        return updateBatchByIdWithoutLogicDelete(entityList, DEFAULT_BATCH_SIZE);
    }

    /**
     * mysql insert on duplicate key update 替代方法
     *
     * @param dataCollect
     *         新數(shù)據(jù)列表
     * @param isFillId
     *         無 ID 是否填充 ID
     * @param uniqueCondition
     *         唯一索引
     * @param updateMapping
     *         更新函數(shù):函數(shù)入?yún)椴樵兊降呐f數(shù)據(jù) & 新數(shù)據(jù)抖剿,出參為更新后的舊數(shù)據(jù)(不能設(shè)置id和相關(guān)唯一索引)
     *
     * @return {@code  List<T>}
     */
    List<T> insertOnUpdateBatch(
            Collection<T> dataCollect,
            boolean isFillId,
            BiFunction<T, LambdaQueryWrapper<T>, LambdaQueryWrapper<T>> uniqueCondition,
            BiFunction<T, T, T> updateMapping
    );

    @Transactional(rollbackFor = Exception.class)
    default List<T> insertOnUpdateBatch(
            Collection<T> dataCollect,
            BiFunction<T, LambdaQueryWrapper<T>, LambdaQueryWrapper<T>> uniqueCondition,
            BiFunction<T, T, T> updateMapping
    ) {
        return insertOnUpdateBatch(dataCollect, true, uniqueCondition, updateMapping);
    }
}
  • 整合后 ServiceImplExtension
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.binding.MapperMethod;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;

public abstract class ServiceImplExtension<M extends BaseMapperExtension<T>, T extends IBaseDO<T>> extends ServiceImpl<M, T> implements IServiceExtension<T> {
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateBatchByIdWithoutLogicDelete(Collection<T> entityList, int batchSize) {
        String sqlStatement = mapperClass.getName() + StringPool.DOT + "updateByIdWithoutLogicDelete";
        // 批量更新優(yōu)化  使用 BATCH 批處理模式
        return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
            param.put(Constants.ENTITY, entity);
            sqlSession.update(sqlStatement, param);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<T> insertOnUpdateBatch(
            Collection<T> dataCollect,
            boolean isFillId,
            BiFunction<T, LambdaQueryWrapper<T>, LambdaQueryWrapper<T>> uniqueCondition,
            BiFunction<T, T, T> updateMapping
    ) {
        return MyBatisPlusUtils.insertOnUpdateBatch(this, dataCollect, isFillId, uniqueCondition, updateMapping);
    }
}

使用案例

基礎(chǔ)實體

繼承 BaseDO

@ApiModel(value = "網(wǎng)絡(luò)拓?fù)鋱D-區(qū)域")
@Data
@TableName(value = "cmdb_topology_region")
public class CmdbTopologyRegion extends BaseDO<CmdbTopologyRegion> {
    @TableField(value = "tenant_code")
    @ApiModelProperty(value = "租戶編碼")
    private Long tenantCode;
}

Mapper 接口

繼承 BaseMapperExtension 接口

public interface CmdbTopologyRegionMapper extends BaseMapperExtension<CmdbTopologyRegion> {
}

Service 接口

繼承 IServiceExtension 接口

@Validated
public interface CmdbTopologyRegionService extends IServiceExtension<CmdbTopologyRegion> {
    @Validated
    int insertOrUpdate(List<CmdbTopologyRegion> regions);
}

Service 實現(xiàn)

@Slf4j
@Service
public class CmdbTopologyRegionServiceImpl extends ServiceImplExtension<CmdbTopologyRegionMapper, CmdbTopologyRegion> implements CmdbTopologyRegionService {
    @Override
    public int insertOrUpdate(List<CmdbTopologyRegion> regions) {
        // regions 為插入更新的數(shù)據(jù)列表
        List<CmdbTopologyRegion> update = insertOnUpdateBatch(regions,
                // 唯一索引查詢條件構(gòu)造,這個案例只是一個唯一索引 TenantCode识窿,如果沒有唯一索引斩郎,直接 return wrapper,如果存在多個唯一索引喻频,使用 wrapper.eq(唯一索引1 構(gòu)造).or(w->w.eq(唯一索引2 構(gòu)造))
                (newData, wrapper) -> wrapper.eq(CmdbTopologyRegion::getTenantCode,"1002"),
                (newData, oldData) -> {
                    // 存在數(shù)據(jù)時候缩宜,會調(diào)用這個更新回調(diào)方法,入?yún)?新數(shù)據(jù)甥温,數(shù)據(jù)庫查詢數(shù)據(jù)),這里需要把 newData 的更新字段值填充進(jìn)去 oldData 返回
                    BeanUtil.copyProperties(newData, oldData, "id","tenantCode");
                    return oldData;
                }
        );
        return update.size();
    }
}

問題回顧

  1. 詳細(xì)查看MySQL 插入更新原理
  2. 不同數(shù)據(jù)庫的語法不一致锻煌,只能使用標(biāo)準(zhǔn) SQL ,所以最好使用業(yè)務(wù)代碼邏輯實現(xiàn)
  3. 詳細(xì)查看 MyBatisPlusUtils 工具類的實現(xiàn)方式
  4. 自定義 SQL 注入器實現(xiàn)
  5. 使用 Mybatis BATCH 批處理模式

優(yōu)點缺點

優(yōu)點

  • 簡單易用姻蚓,支持原有 MybatisPlusLambda 表達(dá)式查詢方式宋梧,不需要自定義 SQL 實現(xiàn)
  • 需要使用插入更新的時候,不需要考慮不同數(shù)據(jù)庫兼容性
  • 有清晰的業(yè)務(wù)代碼邏輯實現(xiàn)狰挡,可以對每條需要更新數(shù)據(jù)的自定義屬性拷貝和轉(zhuǎn)換捂龄,同時具備對更新數(shù)據(jù)進(jìn)行回調(diào)
  • 對原有代碼改造少:把原來繼承實現(xiàn)的基礎(chǔ)模板替換(BaseMapper ,IService ,ServiceImpl

缺點

  • 執(zhí)行速度慢释涛,SQL 執(zhí)行數(shù)量變多,原來只需要一條 SQL 就可以實現(xiàn)批量插入更新跺讯,現(xiàn)在需要多條 SQL (包括查詢枢贿、插入和更新語句)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刀脏,隨后出現(xiàn)的幾起案子局荚,更是在濱河造成了極大的恐慌,老刑警劉巖愈污,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耀态,死亡現(xiàn)場離奇詭異,居然都是意外死亡暂雹,警方通過查閱死者的電腦和手機(jī)首装,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杭跪,“玉大人仙逻,你說我怎么就攤上這事〗颍” “怎么了系奉?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長姑廉。 經(jīng)常有香客問我缺亮,道長,這世上最難降的妖魔是什么桥言? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任萌踱,我火速辦了婚禮,結(jié)果婚禮上号阿,老公的妹妹穿的比我還像新娘并鸵。我一直安慰自己,他們只是感情好扔涧,可當(dāng)我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布园担。 她就那樣靜靜地躺著,像睡著了一般扰柠。 火紅的嫁衣襯著肌膚如雪粉铐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天卤档,我揣著相機(jī)與錄音蝙泼,去河邊找鬼。 笑死劝枣,一個胖子當(dāng)著我的面吹牛汤踏,可吹牛的內(nèi)容都是我干的织鲸。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼溪胶,長吁一口氣:“原來是場噩夢啊……” “哼搂擦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起哗脖,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤瀑踢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后才避,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橱夭,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年桑逝,在試婚紗的時候發(fā)現(xiàn)自己被綠了棘劣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡楞遏,死狀恐怖茬暇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寡喝,我是刑警寧澤糙俗,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站拘荡,受9級特大地震影響臼节,放射性物質(zhì)發(fā)生泄漏撬陵。R本人自食惡果不足惜珊皿,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巨税。 院中可真熱鬧蟋定,春花似錦、人聲如沸草添。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽远寸。三九已至抄淑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驰后,已是汗流浹背肆资。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留灶芝,地道東北人郑原。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓唉韭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親犯犁。 傳聞我的和親對象是個殘疾皇子属愤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容