All Downloads are FREE. Search and download functionalities are using the official Maven repository.

app.myoss.cloud.mybatis.repository.service.impl.BaseCrudServiceImpl Maven / Gradle / Ivy

/*
 * Copyright 2018-2018 https://github.com/myoss
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package app.myoss.cloud.mybatis.repository.service.impl;

import static app.myoss.cloud.mybatis.repository.utils.DbUtils.checkDBResult;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import app.myoss.cloud.core.constants.MyossConstants;
import app.myoss.cloud.core.lang.bean.BeanUtil;
import app.myoss.cloud.core.lang.concurrent.CallableFunc;
import app.myoss.cloud.core.lang.dto.Order;
import app.myoss.cloud.core.lang.dto.Page;
import app.myoss.cloud.core.lang.dto.Result;
import app.myoss.cloud.core.lang.dto.Sort;
import app.myoss.cloud.mybatis.constants.MybatisConstants;
import app.myoss.cloud.mybatis.mapper.template.CrudMapper;
import app.myoss.cloud.mybatis.repository.entity.LogicDeleteEntity;
import app.myoss.cloud.mybatis.repository.entity.PrimaryKeyEntity;
import app.myoss.cloud.mybatis.repository.service.CrudService;
import app.myoss.cloud.mybatis.table.TableColumnInfo;
import app.myoss.cloud.mybatis.table.TableInfo;
import app.myoss.cloud.mybatis.table.TableMetaObject;
import lombok.extern.slf4j.Slf4j;

/**
 * 实现数据库表增、删、改、查常用操作的基类
 *
 * @param  "实体类"的 Mapper Interface 接口
 * @param  实体类
 * @author Jerry.Chen
 * @since 2018年5月9日 下午2:09:18
 */
@Slf4j
public class BaseCrudServiceImpl, T> implements CrudService {
    protected Class            mapperClass;
    protected Class            entityClass;
    protected TableInfo           tableInfo;
    /**
     * 表中的所有字段,用于校验字段名,防止sql注入
     */
    protected Map fieldColumns;
    protected M                   crudMapper;

    /**
     * 初始化实现数据库表增、删、改、查常用操作的基类
     */
    public BaseCrudServiceImpl() {
        Class clazz = this.getClass();
        Type genType = clazz.getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        this.mapperClass = (Class) params[0];
        this.entityClass = (Class) params[1];
    }

    /**
     * 使用 Spring 自动注入"实体类"的 Mapper Interface 接口代理对象
     *
     * @param crudMapper "实体类"的 Mapper Interface 接口代理对象
     */
    @Autowired
    public void setCrudMapper(M crudMapper) {
        this.crudMapper = crudMapper;
        this.tableInfo = TableMetaObject.getTableInfo(this.entityClass);
        if (this.tableInfo != null) {
            this.fieldColumns = Collections.unmodifiableMap(this.tableInfo.getColumns().stream().collect(
                    Collectors.toMap(TableColumnInfo::getProperty, TableColumnInfo::getActualColumn)));
        } else {
            log.error("[{}] getTableInfo failed in [{}]", this.entityClass, this.getClass());
        }
    }

    /**
     * 检查待保存的记录的字段是否有null值
     *
     * @param result 执行结果
     * @param record 实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean checkNull4Create(Result result, T record, Object optionParam) {
        if (!result.isSuccess()) {
            return false;
        }
        if (record == null) {
            result.setSuccess(false).setErrorCode(MybatisConstants.VALUE_IS_BLANK).setErrorMsg("实体对象不能为空");
        }
        return result.isSuccess();
    }

    /**
     * 检查主键字段是否为空
     *
     * @param sqlCommandType 执行的 SQL 命令类型
     * @param result 执行结果
     * @param id 主键值
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean checkPrimaryKeyIsNull(SqlCommandType sqlCommandType, Result result, Serializable id) {
        if (!result.isSuccess()) {
            return false;
        }
        if (id == null) {
            result.setSuccess(false).setErrorCode(MybatisConstants.VALUE_IS_BLANK).setErrorMsg("主键字段不能为空");
        }
        return result.isSuccess();
    }

    /**
     * 检查实体和主键字段是否为空
     *
     * @param sqlCommandType 执行的 SQL 命令类型
     * @param record 实体对象
     * @param checkAll 检查所有的主键字段值是否为空(false: 只要有一个主键字段为空,则校验失败)
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean checkPrimaryKeyIsNull(SqlCommandType sqlCommandType, Object record, boolean checkAll) {
        boolean isNull = record == null;
        if (!isNull) {
            int nullCount = 0;
            Set primaryKeyColumns = tableInfo.getPrimaryKeyColumns();
            for (TableColumnInfo columnInfo : primaryKeyColumns) {
                Object value = BeanUtil.methodInvoke(columnInfo.getPropertyDescriptor().getReadMethod(), record);
                if (value == null) {
                    nullCount++;
                } else if (value instanceof CharSequence && StringUtils.isBlank((CharSequence) value)) {
                    nullCount++;
                }
                if (nullCount > 0 && !checkAll) {
                    isNull = true;
                    break;
                }
            }
            if (checkAll && nullCount > 0 && nullCount == primaryKeyColumns.size()) {
                isNull = true;
            }
        }
        return isNull;
    }

    /**
     * 检查实体和主键字段是否为空
     *
     * @param sqlCommandType 执行的 SQL 命令类型
     * @param result 执行结果
     * @param record 实体对象
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean checkPrimaryKeyIsNull(SqlCommandType sqlCommandType, Result result, Object record) {
        if (!result.isSuccess()) {
            return false;
        }
        boolean isNull = checkPrimaryKeyIsNull(sqlCommandType, record, true);
        if (isNull) {
            result.setSuccess(false).setErrorCode(MybatisConstants.VALUE_IS_BLANK).setErrorMsg("主键字段不能为空");
        }
        return result.isSuccess();
    }

    /**
     * 检查通用查询条件字段是否为空,这里只检查主键id是否为空,防止全表扫描
     *
     * @param sqlCommandType 执行的 SQL 命令类型
     * @param result 执行结果
     * @param condition 查询条件
     * @param extraCondition 扩展查询条件,需要自定义
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean checkCommonQueryConditionIsAllNull(SqlCommandType sqlCommandType, Result result, T condition,
                                                         Map extraCondition) {
        if (!result.isSuccess()) {
            return false;
        }
        return checkPrimaryKeyIsNull(sqlCommandType, result, condition);
    }

    /**
     * 校验分页查询条件字段是否有空值,默认不做任何校验,子类去重写
     *
     * @param condition 分页查询条件
     * @param result 分页查询返回结果
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean checkPageConditionIsAllNull(Page condition, Page result) {
        // 分页查询,默认不做任何校验
        return result.isSuccess();
    }

    /**
     * 检查待保存的记录的字段是否符合预期的格式
     *
     * @param result 执行结果
     * @param record 实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean validFieldValue(Result result, T record, Object optionParam) {
        if (!result.isSuccess()) {
            return false;
        }
        if (record == null) {
            result.setSuccess(false).setErrorCode(MybatisConstants.VALUE_IS_BLANK).setErrorMsg("实体对象不能为空");
        }
        return result.isSuccess();
    }

    /**
     * 检查待保存的记录的字段是否符合预期的格式
     *
     * @param result 执行结果
     * @param record 实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean validFieldValue(Result result, Map record, Object optionParam) {
        if (!result.isSuccess()) {
            return false;
        }
        if (record == null) {
            result.setSuccess(false).setErrorCode(MybatisConstants.VALUE_IS_BLANK).setErrorMsg("实体对象不能为空");
        }
        return result.isSuccess();
    }

    /**
     * 检查待保存的记录的字段是否符合预期的格式
     *
     * @param result 执行结果
     * @param record 实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @return true: 校验成功; false: 校验失败
     */
    protected boolean createValidate(Result result, T record, Object optionParam) {
        if (!checkNull4Create(result, record, optionParam)) {
            return false;
        }
        return validFieldValue(result, record, optionParam);
    }

    /**
     * 将 {@code Map} 中的 {@code key} 转换成数据库字段名,会校验数据库字段名,防止SQL注入
     *
     * @param record 待更新的实体对象,key:是数据库列名,value:是数据库列的值
     * @return 数据库字段列表
     */
    protected Map convertToUpdateUseMap(Map record) {
        Map updateMap = new HashMap<>(record.size());
        for (Entry entry : record.entrySet()) {
            String key = entry.getKey();
            String columnName = fieldColumns.get(key);
            if (columnName != null) {
                // 校验字段名,防止SQL注入
                updateMap.put(columnName, entry.getValue());
            } else {
                log.error("[{}] ignored invalid filed: {}", this.getClass(), key);
            }
        }
        return updateMap;
    }

    /**
     * 将实体类排序字段转换成数据库字段排序,会校验数据库字段名,防止SQL注入
     *
     * @param sort 实体类排序字段
     * @return 数据库字段排序
     */
    protected List convertToOrders(Sort sort) {
        if (sort == null || CollectionUtils.isEmpty(sort.getOrders())) {
            return null;
        }
        List orders = new ArrayList<>(sort.getOrders().size());
        for (Order item : sort.getOrders()) {
            String columnName = fieldColumns.get(item.getProperty());
            if (columnName != null) {
                // 校验字段名,防止SQL注入
                Order order = new Order(item.getDirection(), columnName);
                orders.add(order);
            } else {
                log.error("[{}] ignored invalid filed: {}", this.getClass(), item.getProperty());
            }
        }
        return orders;
    }

    /**
     * 查询存在的记录,用于"检查待保存的实体对象是否已经有存在相同的记录(幂等校验)"
     *
     * @param result 执行结果
     * @param record 待保存的实体对象
     * @return 存在的记录
     * @see #checkRecordIfExist4Create(Result, Object)
     * @see #checkRecordIfExist4Update(Result, Object)
     */
    protected List findExistRecord4CheckRecord(Result result, T record) {
        return null;
    }

    /**
     * 检查待保存的实体对象是否已经有存在相同的记录(幂等校验)
     *
     * @param result 执行结果
     * @param record 实体对象
     * @return true: 存在相同记录, false: 不存在相同记录
     * @see #findExistRecord4CheckRecord(Result, Object)
     */
    protected boolean checkRecordIfExist4Create(Result result, T record) {
        List exists = findExistRecord4CheckRecord(result, record);
        if (CollectionUtils.isEmpty(exists)) {
            return false;
        }
        result.setSuccess(false).setErrorCode(MybatisConstants.MORE_RECORDS);
        T item = exists.get(0);
        StringBuilder errorMsg = new StringBuilder();
        Iterator iterator = tableInfo.getPrimaryKeyColumns().iterator();
        boolean hasNext = iterator.hasNext();
        while (hasNext) {
            TableColumnInfo columnInfo = iterator.next();
            Object value = BeanUtil.methodInvoke(columnInfo.getPropertyDescriptor().getReadMethod(), item);
            errorMsg.append(columnInfo.getProperty()).append("=").append(value);
            hasNext = iterator.hasNext();
            if (hasNext) {
                errorMsg.append(", ");
            }
        }
        if (errorMsg.length() > 0) {
            errorMsg.insert(0, "已经存在相同的记录,主键值[").append("]");
            result.setErrorMsg(errorMsg.toString());
        } else {
            result.setErrorMsg("已经存在相同的记录");
        }
        return true;
    }

    /**
     * 检查待更新的实体对象是否已经有存在相同的记录(幂等校验)。如果实体的类型是 {@link LogicDeleteEntity}
     * ,如果数据是逻辑删除,则校验通过。
     *
     * @param result 执行结果
     * @param record 实体对象
     * @return true: 存在相同记录, false: 不存在相同记录
     * @see #findExistRecord4CheckRecord(Result, Object)
     */
    protected boolean checkRecordIfExist4Update(Result result, T record) {
        if (record instanceof LogicDeleteEntity
                && StringUtils.equals(((LogicDeleteEntity) record).getIsDeleted(), MyossConstants.Y)) {
            return false;
        }
        List exists = findExistRecord4CheckRecord(result, record);
        if (CollectionUtils.isEmpty(exists)) {
            return false;
        }
        if (record instanceof PrimaryKeyEntity) {
            // 使用主键实体类的字段比较
            Serializable id = ((PrimaryKeyEntity) record).getPrimaryKey();
            for (T item : exists) {
                Serializable id1 = ((PrimaryKeyEntity) item).getPrimaryKey();
                if (!id1.equals(id)) {
                    result.setSuccess(false).setErrorCode(MybatisConstants.MORE_RECORDS).setErrorMsg(
                            "已经存在相同的记录,主键id=" + id1);
                    return true;
                }
            }
            return false;
        }

        Set primaryKeyColumns = tableInfo.getPrimaryKeyColumns();
        if (!CollectionUtils.isEmpty(primaryKeyColumns)) {
            // 使用主键字段比较
            List sourceValues = new ArrayList<>(primaryKeyColumns.size());
            for (TableColumnInfo columnInfo : primaryKeyColumns) {
                Object value = BeanUtil.methodInvoke(columnInfo.getPropertyDescriptor().getReadMethod(), record);
                sourceValues.add(value);
            }
            for (T item : exists) {
                List itemValues = new ArrayList<>(primaryKeyColumns.size());
                for (TableColumnInfo columnInfo : primaryKeyColumns) {
                    Object value = BeanUtil.methodInvoke(columnInfo.getPropertyDescriptor().getReadMethod(), item);
                    itemValues.add(value);
                }
                if (!Objects.deepEquals(sourceValues, itemValues)) {
                    StringBuilder errorMsg = new StringBuilder("已经存在相同的记录,主键值[");
                    int idx = 0;
                    for (TableColumnInfo columnInfo : primaryKeyColumns) {
                        Object value = itemValues.get(idx++);
                        errorMsg.append(columnInfo.getProperty()).append("=").append(value);
                        if (idx < itemValues.size()) {
                            errorMsg.append(", ");
                        }
                    }
                    result.setErrorMsg(errorMsg.append("]").toString());
                }
            }
        } else {
            // 不支持,需要之类重写
            throw new UnsupportedOperationException();
        }
        return false;
    }

    /**
     * 检查待更新的实体对象是否已经有存在相同的记录(幂等校验)。默认未实现逻辑,请子类中重写。
     *
     * @param result 执行结果
     * @param record 实体对象
     * @return true: 存在相同记录, false: 不存在相同记录
     * @see #findExistRecord4CheckRecord(Result, Object)
     */
    protected boolean checkRecordIfExist4Update(Result result, Map record) {
        return false;
    }

    /**
     * 保存新记录的时候,设置通用字段的值
     *
     * @param record 待保存的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     */
    protected void setValue4Create(T record, Object optionParam) {

    }

    /**
     * 更新记录的时候,设置通用字段的值
     *
     * @param record 待更新的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     */
    protected void setValue4Update(T record, Object optionParam) {

    }

    /**
     * 更新记录的时候,设置通用字段的值
     *
     * @param record 待更新的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     */
    protected void setValue4Update(Map record, Object optionParam) {

    }

    /**
     * 设置分页查询返回值需要的额外字段,子类去重写
     *
     * @param condition 分页查询条件
     * @param result 分页查询返回结果
     */
    protected void addPageExtraInfo(Page condition, Page result) {

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public  Result create(T record, Object optionParam) {
        Result result = new Result<>();
        boolean validate = createValidate(result, record, optionParam);
        if (!validate) {
            return result;
        }
        return createCallable(result, record, optionParam, () -> create(result, record, optionParam));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public  Result create(T record) {
        return create(record, null);
    }

    /**
     * 用于重写创建的方法,比如加锁创建
     *
     * @param result 保存的结果
     * @param record 待保存的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @param createCallFunc 保存方法回调类,参考:{@link #create(Result, Object, Object)}
     * @param  主键类型
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected  Result createCallable(Result result, T record, Object optionParam,
                                           CallableFunc> createCallFunc) {
        return createCallFunc.call();
    }

    /**
     * 创建新的记录,{@link #create(Object)} 方法的最后一步调用
     *
     * @param result 保存的结果
     * @param record 待保存的实体对象
     * @param  主键类型
     * @param optionParam 可选参数,默认为 {@code null }
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    @SuppressWarnings("unchecked")
    protected  Result create(Result result, T record, Object optionParam) {
        boolean ifExist = checkRecordIfExist4Create(result, record);
        if (!ifExist && result.isSuccess()) {
            setValue4Create(record, optionParam);
            boolean flag = checkDBResult(crudMapper.insert(record));
            if (flag) {
                Set primaryKeyColumns = tableInfo.getPrimaryKeyColumns();
                int size = primaryKeyColumns.size();
                if (size == 0) {
                    // 忽略没有主键字段的情况
                    return result;
                }
                Object value = (size == 1 ? null : new Object[size]);
                int idx = 0;
                for (TableColumnInfo columnInfo : primaryKeyColumns) {
                    I tmp = BeanUtil.methodInvoke(columnInfo.getPropertyDescriptor().getReadMethod(), record);
                    if (size > 1) {
                        // 如果有多个主键字段,使用数组返回
                        ((Object[]) value)[idx++] = tmp;
                    } else {
                        value = tmp;
                    }
                }
                result.setValue((I) value);
            } else {
                result.setSuccess(false).setErrorCode(MybatisConstants.INSERT_DB_FAILED).setErrorMsg("插入失败,请检查");
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result createBatch(List records, Object optionParam) {
        Result result = new Result<>(false);
        if (CollectionUtils.isEmpty(records)) {
            return result.setValue(true);
        }
        return createBatchCallable(result, records, optionParam, () -> createBatch(result, records, optionParam));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result createBatch(List records) {
        return createBatch(records, null);
    }

    /**
     * 用于重写创建的方法,比如加锁创建
     *
     * @param result 保存的结果
     * @param records 待保存的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @param createCallFunc 保存方法回调类,参考:
     *            {@link #createBatch(Result, List, Object)}
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result createBatchCallable(Result result, List records, Object optionParam,
                                                  CallableFunc> createCallFunc) {
        return createCallFunc.call();
    }

    /**
     * 创建新的记录,{@link #createBatch(List)} 方法的最后一步调用
     *
     * @param result 保存的结果
     * @param records 待保存的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result createBatch(Result result, List records, Object optionParam) {
        for (T record : records) {
            if (!createValidate(result, record, optionParam)) {
                return result;
            }
            boolean ifExist = checkRecordIfExist4Create(result, record);
            if (ifExist || !result.isSuccess()) {
                return result;
            }
        }
        // 先校验完数据格式,再设置字段的值
        for (T record : records) {
            setValue4Create(record, optionParam);
            boolean flag = checkDBResult(crudMapper.insert(record));
            if (!flag) {
                return result.setSuccess(false).setErrorCode(MybatisConstants.INSERT_DB_FAILED).setErrorMsg(
                        "插入失败,请检查。[" + record + "]");
            }
        }
        return result.setValue(true);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result updateByPrimaryKey(T record, Object optionParam) {
        Result result = new Result<>(false);
        checkPrimaryKeyIsNull(SqlCommandType.UPDATE, result, record);
        validFieldValue(result, record, optionParam);
        if (!result.isSuccess()) {
            return result;
        }
        return updateByPrimaryKeyCallable(result, record, optionParam,
                () -> updateByPrimaryKey(result, record, optionParam));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result updateByPrimaryKey(T record) {
        return updateByPrimaryKey(record, null);
    }

    /**
     * 用于重写更新的方法,比如加锁更新
     *
     * @param record 待更新的实体对象
     * @param result 更新的结果
     * @param optionParam 可选参数,默认为 {@code null }
     * @param updateCallFunc 更新方法回调类,参考:
     *            {@link #updateByPrimaryKey(Result, Object, Object)}
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result updateByPrimaryKeyCallable(Result result, T record, Object optionParam,
                                                         CallableFunc> updateCallFunc) {
        return updateCallFunc.call();
    }

    /**
     * 更新记录,{@link #updateByPrimaryKey(Object)} 方法的最后一步调用
     *
     * @param result 更新的结果
     * @param record 待更新的实体对象
     * @param optionParam 可选参数,默认为 {@code null }
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result updateByPrimaryKey(Result result, T record, Object optionParam) {
        boolean ifExist = checkRecordIfExist4Update(result, record);
        if (!ifExist && result.isSuccess()) {
            setValue4Update(record, optionParam);
            boolean flag = checkDBResult(crudMapper.updateByPrimaryKey(record));
            if (!flag) {
                result.setSuccess(false).setErrorCode(MybatisConstants.NOT_MATCH_RECORDS).setErrorMsg("更新失败,未匹配到相应的记录");
            } else {
                result.setValue(true);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result updateByCondition(T record, T condition, Object optionParam) {
        Result result = new Result<>(false);
        validFieldValue(result, record, optionParam);
        checkCommonQueryConditionIsAllNull(SqlCommandType.UPDATE, result, condition, null);
        if (!result.isSuccess()) {
            return result;
        }
        return updateByConditionCallable(result, record, condition, optionParam,
                () -> updateByCondition(result, record, condition, optionParam));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result updateByCondition(T record, T condition) {
        return updateByCondition(record, condition, null);
    }

    /**
     * 用于重写更新的方法,比如加锁更新
     *
     * @param result 更新的结果
     * @param record 待更新的实体对象
     * @param condition 匹配的条件
     * @param optionParam 可选参数,默认为 {@code null }
     * @param updateCallFunc 更新方法回调类,参考:
     *            {@link #updateByCondition(Result, Object, Object, Object)}
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result updateByConditionCallable(Result result, T record, T condition,
                                                        Object optionParam,
                                                        CallableFunc> updateCallFunc) {
        return updateCallFunc.call();
    }

    /**
     * 更新记录,{@link #updateByCondition(Object, Object)} 方法的最后一步调用
     *
     * @param result 更新的结果
     * @param record 待更新的实体对象
     * @param condition 匹配的条件
     * @param optionParam 可选参数,默认为 {@code null }
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result updateByCondition(Result result, T record, T condition, Object optionParam) {
        boolean ifExist = checkRecordIfExist4Update(result, record);
        if (!ifExist && result.isSuccess()) {
            setValue4Update(record, optionParam);
            boolean flag = checkDBResult(crudMapper.updateByCondition(record, condition));
            if (!flag) {
                result.setSuccess(false).setErrorCode(MybatisConstants.NOT_MATCH_RECORDS).setErrorMsg("更新失败,未匹配到相应的记录");
            } else {
                result.setValue(true);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result updateUseMapByCondition(Map record, T condition, Object optionParam) {
        Result result = new Result<>(false);
        if (!validFieldValue(result, record, optionParam)) {
            return result;
        }
        checkCommonQueryConditionIsAllNull(SqlCommandType.UPDATE, result, condition, null);
        if (!result.isSuccess()) {
            return result;
        }
        return updateUseMapByConditionCallable(result, record, condition, optionParam,
                () -> updateUseMapByCondition(result, record, condition, optionParam));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result updateUseMapByCondition(Map record, T condition) {
        return updateUseMapByCondition(record, condition, null);
    }

    /**
     * 用于重写更新的方法,比如加锁更新
     *
     * @param result 更新的结果
     * @param record 待更新的实体对象,key:是数据库列名,value:是数据库列的值
     * @param condition 匹配的条件
     * @param optionParam 可选参数,默认为 {@code null }
     * @param updateCallFunc 更新方法回调类,参考:
     *            {@link #updateUseMapByCondition(Result, Map, Object, Object)}
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result updateUseMapByConditionCallable(Result result, Map record,
                                                              T condition, Object optionParam,
                                                              CallableFunc> updateCallFunc) {
        return updateCallFunc.call();
    }

    /**
     * 更新记录,{@link #updateByCondition(Object, Object)} 方法的最后一步调用
     *
     * @param result 更新的结果
     * @param record 待更新的实体对象,key:是数据库列名,value:是数据库列的值
     * @param condition 匹配的条件
     * @param optionParam 可选参数,默认为 {@code null }
     * @return 返回执行结果,默认返回的是 {@code result } 参数,可以被子类覆盖重写
     */
    protected Result updateUseMapByCondition(Result result, Map record, T condition,
                                                      Object optionParam) {
        boolean ifExist = checkRecordIfExist4Update(result, record);
        if (!ifExist && result.isSuccess()) {
            setValue4Update(record, optionParam);
            Map updateMap = convertToUpdateUseMap(record);
            boolean flag = checkDBResult(crudMapper.updateUseMapByCondition(updateMap, condition));
            if (!flag) {
                result.setSuccess(false).setErrorCode(MybatisConstants.NOT_MATCH_RECORDS).setErrorMsg("更新失败,未匹配到相应的记录");
            } else {
                result.setValue(true);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result deleteByPrimaryKey(T condition, Object optionParam) {
        Result result = new Result<>(false);
        if (checkPrimaryKeyIsNull(SqlCommandType.DELETE, result, condition)) {
            boolean flag = checkDBResult(crudMapper.deleteWithPrimaryKey(condition));
            if (!flag) {
                result.setSuccess(false).setErrorCode(MybatisConstants.NOT_MATCH_RECORDS).setErrorMsg("更新失败,未匹配到相应的记录");
            } else {
                result.setValue(true);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result deleteByPrimaryKey(T condition) {
        return deleteByPrimaryKey(condition, null);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result deleteByCondition(T condition, Object optionParam) {
        Result result = new Result<>(false);
        if (checkCommonQueryConditionIsAllNull(SqlCommandType.DELETE, result, condition, null)) {
            boolean flag = checkDBResult(crudMapper.deleteByCondition(condition));
            if (!flag) {
                result.setSuccess(false).setErrorCode(MybatisConstants.NOT_MATCH_RECORDS).setErrorMsg("更新失败,未匹配到相应的记录");
            } else {
                result.setValue(true);
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result deleteByCondition(T condition) {
        return deleteByCondition(condition, null);
    }

    @Override
    public Result findByPrimaryKey(Serializable id) {
        Result result = new Result<>();
        if (checkPrimaryKeyIsNull(SqlCommandType.SELECT, result, id)) {
            T entity = crudMapper.selectByPrimaryKey(id);
            result.setValue(entity);
        }
        return result;
    }

    @Override
    public Result findByPrimaryKey(T condition) {
        Result result = new Result<>();
        if (checkPrimaryKeyIsNull(SqlCommandType.SELECT, result, condition)) {
            T entity = crudMapper.selectWithPrimaryKey(condition);
            result.setValue(entity);
        }
        return result;
    }

    @Override
    public Result findOne(T condition) {
        Result result = new Result<>();
        if (checkCommonQueryConditionIsAllNull(SqlCommandType.SELECT, result, condition, null)) {
            T one = crudMapper.selectOne(condition);
            result.setValue(one);
        }
        return result;
    }

    @Override
    public Result> findList(T condition) {
        Result> result = new Result<>();
        if (checkCommonQueryConditionIsAllNull(SqlCommandType.SELECT, result, condition, null)) {
            List list = crudMapper.selectList(condition);
            result.setValue(list);
        }
        return result;
    }

    @Override
    public Result> findListWithSort(Page condition) {
        Result> result = new Result<>();
        T param = condition.getParam();
        Map extraInfo = condition.getExtraInfo();
        if (checkCommonQueryConditionIsAllNull(SqlCommandType.SELECT, result, param, extraInfo)) {
            Sort sort = condition.getSort();
            List orders = convertToOrders(sort);
            List list = crudMapper.selectListWithSort2(param, extraInfo, orders);
            result.setValue(list);
        }
        return result;
    }

    @Override
    public Result findCount(T condition) {
        Result result = new Result<>();
        if (checkCommonQueryConditionIsAllNull(SqlCommandType.SELECT, result, condition, null)) {
            int count = crudMapper.selectCount(condition);
            result.setValue(count);
        }
        return result;
    }

    @Override
    public Result findCount(T condition, Map extraCondition) {
        Result result = new Result<>();
        if (checkCommonQueryConditionIsAllNull(SqlCommandType.SELECT, result, condition, extraCondition)) {
            int count = crudMapper.selectCount2(condition, extraCondition);
            result.setValue(count);
        }
        return result;
    }

    @Override
    public Page findPage(Page condition) {
        Page result = new Page<>();
        if (!checkPageConditionIsAllNull(condition, result)) {
            return result;
        }

        int pageSize = condition.getPageSize();
        int pageNum = condition.getPageNum();
        int dbPageNum = Math.max(0, pageNum - 1);
        int pageStart = dbPageNum * pageSize;
        T param = condition.getParam();
        Sort sort = condition.getSort();
        List orders = convertToOrders(sort);
        Map extraInfo = condition.getExtraInfo();
        List details = crudMapper.selectPage2(param, extraInfo, pageStart, pageSize, orders);
        int totalCount = crudMapper.selectCount2(param, extraInfo);
        // 设置额外字段
        addPageExtraInfo(condition, result);

        result.setValue(details).setTotalCount(totalCount).setPageNum(dbPageNum + 1).setPageSize(pageSize);
        return result;
    }
}