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

cn.org.atool.fluent.mybatis.base.crud.BaseSqlProvider Maven / Gradle / Ivy

package cn.org.atool.fluent.mybatis.base.crud;

import cn.org.atool.fluent.mybatis.If;
import cn.org.atool.fluent.mybatis.base.BatchCrud;
import cn.org.atool.fluent.mybatis.base.IEntity;
import cn.org.atool.fluent.mybatis.base.entity.IMapping;
import cn.org.atool.fluent.mybatis.base.mapper.IEntityMapper;
import cn.org.atool.fluent.mybatis.base.model.FieldType;
import cn.org.atool.fluent.mybatis.base.model.InsertList;
import cn.org.atool.fluent.mybatis.exception.FluentMybatisException;
import cn.org.atool.fluent.mybatis.mapper.MapperSql;
import cn.org.atool.fluent.mybatis.metadata.DbType;
import cn.org.atool.fluent.mybatis.segment.model.WrapperData;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static cn.org.atool.fluent.mybatis.If.notBlank;
import static cn.org.atool.fluent.mybatis.mapper.FluentConst.*;
import static cn.org.atool.fluent.mybatis.mapper.StrConstant.EMPTY;
import static cn.org.atool.fluent.mybatis.utility.MybatisUtil.*;
import static cn.org.atool.fluent.mybatis.utility.SqlProviderUtils.*;
import static java.lang.Integer.min;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;

/**
 * SqlProvider: 动态SQL构造基类
 *
 * @author wudarui
 */
@SuppressWarnings({"rawtypes", "unused"})
public abstract class BaseSqlProvider {
    /**
     * 批量更新, 插入, 删除操作语句构造
     * {@link IEntityMapper#batchCrud(BatchCrud)}
     *
     * @param map k-v条件
     * @return ignore
     */
    public static String batchCrud(Map map) {
        IWrapper wrapper = getWrapper(map, Param_EW);
        if (!(wrapper instanceof BatchCrudImpl)) {
            throw new IllegalArgumentException("the wrapper should be an instance of BatchUpdaterImpl.");
        }
        return ((BatchCrudImpl) wrapper).batchSql();
    }

    /**
     * 构造{@link IEntityMapper#insertSelect(String[], IQuery)}SQL语句
     *
     * @param map k-v条件
     * @return ignore
     */
    public String insertSelect(Map map) {
        String[] fields = (String[]) map.get(Param_Fields);
        IQuery query = (IQuery) map.get(Param_EW);
        return buildInsertSelect(dbType(), this.tableName(), fields, query);
    }

    public static String buildInsertSelect(DbType dbType, String tableName, String[] fields, IQuery query) {
        assertNotBlank("tableName", tableName);
        assertNotEmpty(Param_Fields, fields);
        assertNotNull(Param_EW, query);
        String columns = Stream.of(fields).map(dbType::wrap).collect(joining(", "));
        return "INSERT INTO " + tableName + " (" + columns + ") " +
            query.getWrapperData().getQuerySql();
    }

    /**
     * 插入id未赋值的entity
     *
     * @param entity 实体实例
     * @return sql
     */
    public String insert(E entity) {
        return buildInsertSql(EMPTY, entity, false);
    }

    /**
     * 插入id已赋值的entity
     *
     * @param entity 实体实例
     * @return sql
     */
    public String insertWithPk(E entity) {
        return buildInsertSql(EMPTY, entity, true);
    }

    public String insertBatch(Map map) {
        assertNotEmpty(Param_List, map);
        List entities = getParas(map, Param_List);
        return this.insertBatch(entities, false);
    }

    public String insertBatchWithPk(Map map) {
        assertNotEmpty(Param_List, map);
        List entities = getParas(map, Param_List);
        return this.insertBatch(entities, true);
    }

    private String insertBatch(List entities, boolean withPk) {
        MapperSql sql = new MapperSql();
        for (E entity : entities) {
            this.validateInsertEntity(entity, withPk);
        }
        sql.INSERT_INTO(this.tableName());
        sql.INSERT_COLUMNS(dbType(), this.allFields(withPk));
        sql.VALUES();
        for (int index = 0; index < entities.size(); index++) {
            if (index > 0) {
                sql.APPEND(", ");
            }
            sql.INSERT_VALUES(this.insertBatchEntity(index, entities.get(index), withPk));
        }
        return sql.toString();
    }

    /**
     * 设置默认值,校验pk设置是否合法
     *
     * @param entity 实体实例
     * @param withPk true: 带id值插入; false: 不带id值插入
     */
    private void validateInsertEntity(E entity, boolean withPk) {
        this.setEntityByDefault(entity);
        if (withPk) {
            isTrue(this.primaryNotNull(entity), "the pk of insert entity can't be null.");
        } else {
            isTrue(this.primaryIsNull(entity), "the pk of insert entity must be null.");
        }
    }

    /**
     * 单个插入时, insert字段和值
     *
     * @param inserts 插入字段值对
     * @param entity  实体实例
     * @param withPk  true:with id; false: without id
     */
    protected abstract void insertEntity(InsertList inserts, String prefix, E entity, boolean withPk);

    /**
     * 批量插入时,第index个实例VALUES SQL构造
     *
     * @param index  序号
     * @param entity 实例
     * @param withPk true:with id; false: without id
     * @return ignore
     */
    protected abstract List insertBatchEntity(int index, E entity, boolean withPk);

    /**
     * entity无主键或者主键值为null
     *
     * @param entity 实体实例
     * @return ignore
     */
    protected abstract boolean primaryIsNull(E entity);

    /**
     * entity无主键或者主键有值
     *
     * @param entity 实体实例
     * @return ignore
     */
    protected abstract boolean primaryNotNull(E entity);

    /**
     * 去掉limit部分 count(IQuery) SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String countNoLimit(Map map) {
        WrapperData ew = getWrapperData(map, Param_EW);
        if (notBlank(ew.getCustomizedSql())) {
            return ew.getCustomizedSql();
        }
        MapperSql sql = new MapperSql();
        sql.COUNT(ew.getTable(), ew);
        sql.WHERE_GROUP_BY(ew);
        return sql.toString();
    }

    /**
     * count(IQuery) SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String count(Map map) {
        WrapperData ew = getWrapperData(map, Param_EW);
        if (notBlank(ew.getCustomizedSql())) {
            return ew.getCustomizedSql();
        }
        MapperSql sql = new MapperSql();
        sql.COUNT(ew.getTable(), ew);
        sql.WHERE_GROUP_ORDER_BY(ew);
        return byPaged(this.dbType(), ew, sql.toString());
    }

    private String queryByWrapperData(Map map) {
        WrapperData ew = getWrapperData(map, Param_EW);
        if (notBlank(ew.getCustomizedSql())) {
            return ew.getCustomizedSql();
        }
        MapperSql sql = new MapperSql();
        sql.SELECT(ew.getTable(), ew, this.joiningAllFields());
        sql.WHERE_GROUP_ORDER_BY(ew);
        return byPaged(this.dbType(), ew, sql.toString());
    }

    /**
     * 根据动态条件查询Entity SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String listEntity(Map map) {
        return this.queryByWrapperData(map);
    }

    /**
     * 根据动态条件查询Map列表 SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String listMaps(Map map) {
        return this.queryByWrapperData(map);
    }

    /**
     * 根据动态条件查询单列数据列表 SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String listObjs(Map map) {
        return this.queryByWrapperData(map);
    }

    /**
     * 根据Map查询数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String listByMap(Map map) {
        Map where = getParas(map, Param_CM);
        assertNotEmpty("where", where);
        MapperSql sql = new MapperSql();
        sql.SELECT(this.tableName(), this.joiningAllFields());
        sql.WHERE(dbType(), Param_CM, where);
        return sql.toString();
    }

    /**
     * 根据Id列表查询数据SQL构造
     *
     * @param map k-v条件
     * @return sql
     */
    public String listByIds(Map map) {
        MapperSql sql = new MapperSql();
        Collection ids = getParas(map, Param_List);
        assertNotEmpty("PrimaryKeyList", ids);
        sql.SELECT(this.tableName(), this.joiningAllFields());
        sql.WHERE_PK_IN(this.dbType().wrap(this.idColumn()), ids.size());
        return sql.toString();
    }

    /**
     * 根据主键查找数据SQL构造
     *
     * @param id 主键
     * @return ignore
     */
    public String findById(Serializable id) {
        assertNotNull("PrimaryKey", id);
        MapperSql sql = new MapperSql();
        sql.SELECT(this.tableName(), this.joiningAllFields());
        sql.WHERE(format("%s = #{value}", dbType().wrap(this.idColumn())));
        return sql.toString();
    }

    private String joiningAllFields() {
        return this.allFields(true).stream()
            .map(dbType()::wrap)
            .collect(joining(", "));
    }

    /**
     * 根据动态条件查询一条记录SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String findOne(Map map) {
        return this.queryByWrapperData(map);
    }

    /**
     * 根据主键物理删除数据SQL构造
     *
     * @param map 主键值
     * @return ignore
     */
    public String deleteById(Map map) {
        Serializable[] ids = (Serializable[]) map.get(Param_List);
        assertNotEmpty("ids", ids);
        MapperSql sql = new MapperSql();
        sql.DELETE_FROM(this.tableName(), null);
        this.whereEqIds(sql, ids);
        return sql.toString();
    }

    /**
     * 根据主键逻辑删除数据SQL构造
     *
     * @param map 参数
     * @return ignore
     */
    public String logicDeleteById(Map map) {
        Serializable[] ids = (Serializable[]) map.get(Param_List);
        assertNotEmpty("ids", ids);
        MapperSql sql = new MapperSql();
        this.logicDelete(sql);
        this.whereEqIds(sql, ids);
        return sql.toString();
    }

    private void whereEqIds(MapperSql sql, Serializable[] ids) {
        String idColumn = this.dbType().wrap(this.idColumn());
        if (ids.length == 1) {
            sql.WHERE(format("%s = #{list[0]}", idColumn));
        } else {
            StringBuilder values = new StringBuilder();
            for (int index = 0; index < ids.length; index++) {
                if (index > 0) {
                    values.append(", ");
                }
                values.append("#{list[").append(index).append("]}");
            }
            sql.WHERE(format("%s IN (%s)", idColumn, values));
        }
    }

    /**
     * 根据主键列表物理删除数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String deleteByIds(Map map) {
        Collection ids = getParas(map, Param_List);
        assertNotEmpty("ids", ids);
        MapperSql sql = new MapperSql();
        sql.DELETE_FROM(this.tableName(), null);
        sql.WHERE_PK_IN(this.dbType().wrap(this.idColumn()), ids.size());
        return sql.toString();
    }

    /**
     * 根据主键列表逻辑删除数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String logicDeleteByIds(Map map) {
        Collection ids = getParas(map, Param_List);
        assertNotEmpty("ids", ids);
        MapperSql sql = new MapperSql();
        this.logicDelete(sql);
        sql.WHERE_PK_IN(this.dbType().wrap(this.idColumn()), ids.size());
        return sql.toString();
    }

    /**
     * 按map删除数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String deleteByMap(Map map) {
        Map cm = getParas(map, Param_CM);
        MapperSql sql = new MapperSql();
        sql.DELETE_FROM(this.tableName(), null);
        this.whereByMap(sql, cm);
        return sql.toString();
    }

    /**
     * 按map逻辑删除数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String logicDeleteByMap(Map map) {
        Map cm = getParas(map, Param_CM);
        MapperSql sql = new MapperSql();
        this.logicDelete(sql);
        this.whereByMap(sql, cm);
        return sql.toString();
    }

    /**
     * 按map构造条件语句
     *
     * @param sql sql构造器
     * @param map key-value条件
     */
    private void whereByMap(MapperSql sql, Map map) {
        List where = new ArrayList<>();
        for (String key : map.keySet()) {
            where.add(format("%s = #{%s.%s}", dbType().wrap(key), Param_CM, key));
        }
        sql.WHERE(where);
    }

    /**
     * 根据动态查询条件物理删除数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String delete(Map map) {
        WrapperData ew = getWrapperData(map, Param_EW);
        return buildDeleteSql(ew, false);
    }

    /**
     * 根据动态查询条件逻辑删除数据SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String logicDelete(Map map) {
        WrapperData ew = getWrapperData(map, Param_EW);
        return buildDeleteSql(ew, true);
    }

    /**
     * update(IQuery) SQL构造
     *
     * @param map k-v条件
     * @return ignore
     */
    public String updateBy(Map map) {
        Object wrapper = map.get(Param_EW);
        if (If.isEmpty(wrapper)) {
            throw FluentMybatisException.instance("the parameter[%s] can't be empty.", Param_EW);
        }
        if (!(wrapper instanceof IUpdate[])) {
            throw new IllegalArgumentException("the parameter should be an array of IUpdate");
        }
        IUpdate[] updaters = (IUpdate[]) wrapper;
        List list = new ArrayList<>(updaters.length);
        int index = 0;
        for (IUpdate updater : updaters) {
            String sql = this.buildUpdaterSql(updater.getWrapperData());
            sql = addEwParaIndex(sql, format("[%d]", index));
            index++;
            list.add(sql);
        }
        return String.join(";\n", list);
    }

    private final static char[] EW_CONST = "#{ew.".toCharArray();

    private final static String sub = ".wrapperData.parameters.";

    /**
     * 替换变量占位符, 增加ew数组下标
     * #{ew.wrapperData.parameters.xxx}替换为#{ew[0].wrapperData.parameters.xxx}
     * 不采用正则表达式方式替换, 是编码方式替换简单,一次字符串扫描即可完成
     *
     * @param sql    sql语句
     * @param aIndex 变量参数序号
     * @return 改写后的sql
     */
    static String addEwParaIndex(String sql, String aIndex) {
        StringBuilder buff = new StringBuilder();
        int match = 0;
        int len = sql.length();
        for (int loop = 0; loop < sql.length(); loop++) {
            char ch = sql.charAt(loop);
            if (match == 4) {
                if (sql.substring(loop, min(sub.length() + loop, len)).equals(sub)) {
                    buff.append(aIndex);
                }
                match = 0;
            } else {
                // 匹配字符串 "#{ew.", 完全匹配上 match=4
                match = EW_CONST[match] == ch ? match + 1 : 0;
            }
            buff.append(ch);
        }
        return buff.toString();
    }

    /**
     * 根据{@link cn.org.atool.fluent.mybatis.base.crud.IDefaultSetter#setInsertDefault(IEntity)}设置默认值
     *
     * @param entity 实体实例
     */
    protected abstract void setEntityByDefault(IEntity entity);

    /**
     * 构造updates中没有显式设置的默认值构造
     *
     * @param updates 显式update字段
     * @return ignore
     */
    protected abstract List updateDefaults(Map updates, boolean ignoreLockVersion);

    /**
     * 构建插入语句
     *
     * @param prefix entity变量前缀
     * @param entity 实体实例
     * @param withPk 包含主键?
     * @return ignore
     */
    public String buildInsertSql(String prefix, E entity, boolean withPk) {
        assertNotNull(Param_Entity, entity);
        this.validateInsertEntity(entity, withPk);
        MapperSql sql = new MapperSql();
        sql.INSERT_INTO(this.tableName());
        InsertList inserts = new InsertList();
        this.insertEntity(inserts, prefix, entity, withPk);
        sql.INSERT_COLUMNS(dbType(), inserts.columns);
        sql.VALUES();
        sql.INSERT_VALUES(inserts.values);
        return sql.toString();
    }

    /**
     * 根据WrapperData设置构建更新语句
     *
     * @param ew 更新/查询 构造数据
     * @return sql
     */
    public String buildUpdaterSql(WrapperData ew) {
        assertNotNull("wrapperData of updater", ew);
        if (notBlank(ew.getCustomizedSql())) {
            return ew.getCustomizedSql();
        }
        Map updates = ew.getUpdates();
        assertNotEmpty("updates", updates);

        MapperSql sql = new MapperSql();
        sql.UPDATE(ew.getTable(), ew);
        List needDefaults = this.updateDefaults(updates, ew.isIgnoreLockVersion());
        // 如果忽略版本锁, 则移除版本锁更新的默认值
        String versionField = this.versionField();
        if (ew.isIgnoreLockVersion() && notBlank(versionField)) {
            needDefaults.remove(this.versionField());
        }
        needDefaults.add(ew.getUpdateStr());
        sql.SET(needDefaults);
        // 如果忽略版本锁, 则跳过版本锁条件检查
        if (!ew.isIgnoreLockVersion()) {
            this.checkUpdateVersionWhere(ew.findWhereColumns());
        }
        sql.WHERE_GROUP_ORDER_BY(ew);
        sql.LIMIT(ew, true);
        return sql.toString();
    }

    /**
     * 更新时, 检查乐观锁字段条件是否设置
     */
    protected void checkUpdateVersionWhere(List wheres) {
        String versionField = this.versionField();
        if (If.notBlank(versionField) &&
            !wheres.contains(versionField) &&
            !wheres.contains(this.dbType().wrap(versionField))) {
            throw new RuntimeException("The version lock field was explicitly set, but no version condition was found in the update condition.");
        }
    }

    /**
     * 根据WrapperData设置构建删除语句
     *
     * @param ew 更新/查询 构造数据
     * @return sql
     */
    public String buildDeleteSql(WrapperData ew, boolean isLogic) {
        if (notBlank(ew.getCustomizedSql())) {
            return ew.getCustomizedSql();
        }
        MapperSql sql = new MapperSql();
        if (isLogic) {
            this.logicDelete(sql);
        } else {
            sql.DELETE_FROM(this.tableName(), ew);
        }
        sql.WHERE_GROUP_ORDER_BY(ew);
        return sql.toString();
    }

    /**
     * 构造 UPDATE table SET logic_delete_field = true 语句
     *
     * @param sql sql构造器
     */
    private void logicDelete(MapperSql sql) {
        assertNotNull("logical delete field of table(" + this.tableName() + ")", this.logicDeleteField());
        sql.UPDATE(this.tableName(), null);
        if (this.longTypeOfLogicDelete()) {
            sql.SET(String.format("%s = %d", dbType().wrap(this.logicDeleteField()), System.currentTimeMillis()));
        } else {
            sql.SET(String.format("%s = true", dbType().wrap(this.logicDeleteField())));
        }
    }

    /**
     * 数据库类型
     *
     * @return ignore
     */
    protected abstract DbType dbType();

    /**
     * 返回表名
     *
     * @return ignore
     */
    protected abstract String tableName();

    /**
     * 所有字段列表(以逗号隔开)
     *
     * @param withPk 是否包含主键字段
     * @return ignore
     */
    protected abstract List allFields(boolean withPk);

    /**
     * 获取字段映射关系
     *
     * @return 字段映射
     */
    protected abstract IMapping mapping();

    /**
     * 主键字段名
     *
     * @return ignore
     */
    private String idColumn() {
        return this.mapping().findField(FieldType.PRIMARY_ID)
            .map(c -> c.column)
            .orElseThrow(() -> new RuntimeException("the primary not found."));
    }

    /**
     * 乐观锁字段
     *
     * @return ignore
     */
    private String versionField() {
        return this.mapping().findField(FieldType.LOCK_VERSION).map(c -> c.column).orElse(null);
    }

    /**
     * 逻辑删除字段
     *
     * @return ignore
     */
    private String logicDeleteField() {
        return this.mapping().findField(FieldType.LOGIC_DELETED).map(c -> c.column).orElse(null);
    }

    /**
     * 逻辑删除字段是否为 Long 型
     *
     * @return ignore
     */
    protected abstract boolean longTypeOfLogicDelete();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy