【mybatis plus源码解析】(二)详解SQL注入器底层原理,mybatis plus是如何实现自动注入CRUD操作

8 篇文章 4 订阅
7 篇文章 4 订阅

系列文章目录

【mybatis plus源码解析】mybatis plus执行原理
【mybatis plus源码解析】(二)详解自定义SQL注入器底层原理


前言

上篇文章介绍了【mybatis plus源码解析】mybatis plus执行原理,这回接着上篇文章,建议看完上篇文章再来看这篇,这里主要介绍相关类

一、ISqlInjector SQL自动注入器接口的相关uml图

uml图

ISqlInjector顶级接口,只做一件事

在这里插入图片描述

再来看看AbstractSqlInjector抽象类

AbstractSqlInjector类实现了inspectInject注入方法

/**
 * SQL 自动注入器
 *
 * @author hubin
 * @since 2018-04-07
 */
public abstract class AbstractSqlInjector implements ISqlInjector {

    protected final Log logger = LogFactory.getLog(this.getClass());

    @Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
    	//通过反射获取实体类对象
        Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
        if (modelClass != null) {
            String className = mapperClass.toString();
            //获取mapperRegistry缓存用于对比,防止覆盖
            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
            //通过实体类对象反射方式获取信息(比如实体类上的注解)封装成TableInfo存储表信息对象
                TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
                List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);//获取要注入的方法集合
                if (CollectionUtils.isNotEmpty(methodList)) {
                    // 循环注入自定义方法
                    methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
                } else {
                    logger.debug(mapperClass.toString() + ", No effective injection method was found.");
                }
                mapperRegistryCache.add(className);
            }
        }
    }

    /**
     * <p>
     * 获取 注入的方法
     * </p>
     *
     * @param mapperClass 当前mapper
     * @return 注入的方法集合
     * @since 3.1.2 add  mapperClass
     */
    public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass,TableInfo tableInfo);

}

DefaultSqlInjector默认SQL 注入器

DefaultSqlInjector实现了AbstractSqlInjector类的getMethodList方法,规定哪些方法可以实现自动注入

/**
 * SQL 默认注入器
 *
 * @author hubin
 * @since 2018-04-10
 */
public class DefaultSqlInjector extends AbstractSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        Stream.Builder<AbstractMethod> builder = Stream.<AbstractMethod>builder()
            .add(new Insert())
            .add(new Delete())
            .add(new DeleteByMap())
            .add(new Update())
            .add(new SelectByMap())
            .add(new SelectCount())
            .add(new SelectMaps())
            .add(new SelectMapsPage())
            .add(new SelectObjs())
            .add(new SelectList())
            .add(new SelectPage());
        if (tableInfo.havePK()) {
            builder.add(new DeleteById())
                .add(new DeleteBatchByIds())
                .add(new UpdateById())
                .add(new SelectById())
                .add(new SelectBatchByIds());
        } else {
            logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
                tableInfo.getEntityType()));
        }
        return builder.build().collect(toList());
    }
}

默认的注入器类治理介绍完了,里面其实做了一件事就是调用注入mapper默认实现的CRUD方法

二、注入方法相关的类

先看看UML图

在这里插入图片描述

AbstractMethod抽象方法类

/**
 * 抽象的注入方法类
 *
 * @author hubin
 * @since 2018-04-06
 */
@SuppressWarnings("serial")
public abstract class AbstractMethod implements Constants {
    protected static final Log logger = LogFactory.getLog(AbstractMethod.class);

    protected Configuration configuration;
    protected LanguageDriver languageDriver;
    protected MapperBuilderAssistant builderAssistant;

    /**
     * 方法名称
     * @since 3.5.0
     */
    protected final String methodName;

    /**
     * @see AbstractMethod#AbstractMethod(java.lang.String)
     * @since 3.5.0
     */
    @Deprecated
    public AbstractMethod() {
        methodName = null;
    }

    /**
     * @param methodName 方法名
     * @since 3.5.0
     */
    protected AbstractMethod(String methodName) {
        Assert.notNull(methodName, "方法名不能为空");
        this.methodName = methodName;
    }

    /**
     * 注入自定义方法
     */
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        /* 注入自定义方法 */
        injectMappedStatement(mapperClass, modelClass, tableInfo);
    }

    /**
     * 是否已经存在MappedStatement
     *
     * @param mappedStatement MappedStatement
     * @return true or false
     */
    private boolean hasMappedStatement(String mappedStatement) {
        return configuration.hasStatement(mappedStatement, false);
    }

    /**
     * SQL 更新 set 语句
     *
     * @param table 表信息
     * @return sql set 片段
     */
    protected String sqlLogicSet(TableInfo table) {
        return "SET " + table.getLogicDeleteSql(false, false);
    }

    /**
     * SQL 更新 set 语句
     *
     * @param logic  是否逻辑删除注入器
     * @param ew     是否存在 UpdateWrapper 条件
     * @param table  表信息
     * @param alias  别名
     * @param prefix 前缀
     * @return sql
     */
    protected String sqlSet(boolean logic, boolean ew, TableInfo table, boolean judgeAliasNull, final String alias,
                            final String prefix) {
        String sqlScript = table.getAllSqlSet(logic, prefix);
        if (judgeAliasNull) {
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", alias), true);
        }
        if (ew) {
            sqlScript += NEWLINE;
            sqlScript += convertIfEwParam(U_WRAPPER_SQL_SET, false);
        }
        sqlScript = SqlScriptUtils.convertSet(sqlScript);
        return sqlScript;
    }

    /**
     * SQL 注释
     *
     * @return sql
     */
    protected String sqlComment() {
        return convertIfEwParam(Q_WRAPPER_SQL_COMMENT, true);
    }

    /**
     * SQL 注释
     *
     * @return sql
     */
    protected String sqlFirst() {
        return convertIfEwParam(Q_WRAPPER_SQL_FIRST, true);
    }

    protected String convertIfEwParam(final String param, final boolean newLine) {
        return SqlScriptUtils.convertIf(SqlScriptUtils.unSafeParam(param),
            String.format("%s != null and %s != null", WRAPPER, param), newLine);
    }

    /**
     * SQL 查询所有表字段
     *
     * @param table        表信息
     * @param queryWrapper 是否为使用 queryWrapper 查询
     * @return sql 脚本
     */
    protected String sqlSelectColumns(TableInfo table, boolean queryWrapper) {
        /* 假设存在用户自定义的 resultMap 映射返回 */
        String selectColumns = ASTERISK;
        if (table.getResultMap() == null || table.isAutoInitResultMap()) {
            /* 未设置 resultMap 或者 resultMap 是自动构建的,视为属于mp的规则范围内 */
            selectColumns = table.getAllSqlSelect();
        }
        if (!queryWrapper) {
            return selectColumns;
        }
        return convertChooseEwSelect(selectColumns);
    }

    /**
     * SQL 查询记录行数
     *
     * @return count sql 脚本
     */
    protected String sqlCount() {
        return convertChooseEwSelect(ASTERISK);
    }

    /**
     * SQL 设置selectObj sql select
     *
     * @param table 表信息
     */
    protected String sqlSelectObjsColumns(TableInfo table) {
        return convertChooseEwSelect(table.getAllSqlSelect());
    }

    protected String convertChooseEwSelect(final String otherwise) {
        return SqlScriptUtils.convertChoose(String.format("%s != null and %s != null", WRAPPER, Q_WRAPPER_SQL_SELECT),
            SqlScriptUtils.unSafeParam(Q_WRAPPER_SQL_SELECT), otherwise);
    }

    /**
     * SQL map 查询条件
     */
    protected String sqlWhereByMap(TableInfo table) {
        if (table.isWithLogicDelete()) {
            // 逻辑删除
            String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ",
                " ${k} = #{v} ");
            sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, "k", "v", "AND");
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and !%s.isEmpty", COLUMN_MAP, COLUMN_MAP), true);
            sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true));
            sqlScript = SqlScriptUtils.convertWhere(sqlScript);
            return sqlScript;
        } else {
            String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ",
                " ${k} = #{v} ");
            sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, "k", "v", "AND");
            sqlScript = SqlScriptUtils.convertWhere(sqlScript);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and !%s", COLUMN_MAP,
                COLUMN_MAP_IS_EMPTY), true);
            return sqlScript;
        }
    }

    /**
     * EntityWrapper方式获取select where
     *
     * @param newLine 是否提到下一行
     * @param table   表信息
     * @return String
     */
    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
        if (table.isWithLogicDelete()) {
            String sqlScript = table.getAllSqlWhere(true, true, WRAPPER_ENTITY_DOT);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY),
                true);
            sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true) + NEWLINE);
            String normalSqlScript = SqlScriptUtils.convertIf(String.format("AND ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                    WRAPPER_NONEMPTYOFNORMAL), true);
            normalSqlScript += NEWLINE;
            normalSqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                    WRAPPER_EMPTYOFNORMAL), true);
            sqlScript += normalSqlScript;
            sqlScript = SqlScriptUtils.convertChoose(String.format("%s != null", WRAPPER), sqlScript,
                table.getLogicDeleteSql(false, true));
            sqlScript = SqlScriptUtils.convertWhere(sqlScript);
            return newLine ? NEWLINE + sqlScript : sqlScript;
        } else {
            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;
        }
    }

    protected String sqlOrderBy(TableInfo tableInfo) {
        /* 不存在排序字段,直接返回空 */
        List<TableFieldInfo> orderByFields = tableInfo.getOrderByFields();
        if (CollectionUtils.isEmpty(orderByFields)) {
            return StringPool.EMPTY;
        }
        orderByFields.sort(Comparator.comparingInt(TableFieldInfo::getOrderBySort));
        StringBuilder sql = new StringBuilder();
        sql.append(NEWLINE).append(" ORDER BY ");
        sql.append(orderByFields.stream().map(tfi -> String.format("%s %s", tfi.getColumn(),
            tfi.getOrderByType())).collect(joining(",")));
        /* 当wrapper中传递了orderBy属性,@orderBy注解失效 */
        return SqlScriptUtils.convertIf(sql.toString(), String.format("%s == null or %s", WRAPPER,
            WRAPPER_EXPRESSION_ORDER), true);
    }

    /**
     * 过滤 TableFieldInfo 集合, join 成字符串
     */
    protected String filterTableFieldInfo(List<TableFieldInfo> fieldList, Predicate<TableFieldInfo> predicate,
                                          Function<TableFieldInfo, String> function, String joiningVal) {
        Stream<TableFieldInfo> infoStream = fieldList.stream();
        if (predicate != null) {
            return infoStream.filter(predicate).map(function).collect(joining(joiningVal));
        }
        return infoStream.map(function).collect(joining(joiningVal));
    }

    /**
     * 获取乐观锁相关
     *
     * @param tableInfo 表信息
     * @return String
     */
    protected String optlockVersion(TableInfo tableInfo) {
        if (tableInfo.isWithVersion()) {
            return tableInfo.getVersionFieldInfo().getVersionOli(ENTITY, ENTITY_DOT);
        }
        return EMPTY;
    }

    /**
     * 查询
     */
    protected MappedStatement addSelectMappedStatementForTable(Class<?> mapperClass, String id, SqlSource sqlSource,
                                                               TableInfo table) {
        String resultMap = table.getResultMap();
        if (null != resultMap) {
            /* 返回 resultMap 映射结果集 */
            return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null,
                resultMap, null, NoKeyGenerator.INSTANCE, null, null);
        } else {
            /* 普通查询 */
            return addSelectMappedStatementForOther(mapperClass, id, sqlSource, table.getEntityType());
        }
    }

    /**
     * 查询
     * @since 3.5.0
     */
    protected MappedStatement addSelectMappedStatementForTable(Class<?> mapperClass, SqlSource sqlSource, TableInfo table) {
        return addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, table);
    }

    /**
     * 查询
     */
    protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, String id, SqlSource sqlSource,
                                                               Class<?> resultType) {
        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null,
            null, resultType, NoKeyGenerator.INSTANCE, null, null);
    }

    /**
     * 查询
     *
     * @since 3.5.0
     */
    protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, SqlSource sqlSource, Class<?> resultType) {
        return addSelectMappedStatementForOther(mapperClass, this.methodName, sqlSource, resultType);
    }

    /**
     * 插入
     */
    protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,
                                                       SqlSource sqlSource, KeyGenerator keyGenerator,
                                                       String keyProperty, String keyColumn) {
        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, parameterType, null,
            Integer.class, keyGenerator, keyProperty, keyColumn);
    }

    /**
     * 插入
     * @since 3.5.0
     */
    protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType,
                                                       SqlSource sqlSource, KeyGenerator keyGenerator,
                                                       String keyProperty, String keyColumn) {
        return addInsertMappedStatement(mapperClass, parameterType, this.methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
    }


    /**
     * 删除
     */
    protected MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {
        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null,
            null, Integer.class, NoKeyGenerator.INSTANCE, null, null);
    }

    /**
     * @since 3.5.0
     */
    protected MappedStatement addDeleteMappedStatement(Class<?> mapperClass, SqlSource sqlSource) {
        return addDeleteMappedStatement(mapperClass, this.methodName, sqlSource);
    }

    /**
     * 更新
     */
    protected MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,
                                                       SqlSource sqlSource) {
        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, parameterType, null,
            Integer.class, NoKeyGenerator.INSTANCE, null, null);
    }

    /**
     * 更新
     *
     * @since 3.5.0
     */
    protected MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> parameterType,
                                                       SqlSource sqlSource) {
        return addUpdateMappedStatement(mapperClass, parameterType, this.methodName, sqlSource);
    }

    /**
     * 添加 MappedStatement 到 Mybatis 容器
     */
    protected MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,
                                                 SqlCommandType sqlCommandType, Class<?> parameterType,
                                                 String resultMap, Class<?> resultType, KeyGenerator keyGenerator,
                                                 String keyProperty, String keyColumn) {
        String statementName = mapperClass.getName() + DOT + id;
        if (hasMappedStatement(statementName)) {
            logger.warn(LEFT_SQ_BRACKET + statementName + "] Has been loaded by XML or SqlProvider or Mybatis's Annotation, so ignoring this injection for [" + getClass() + RIGHT_SQ_BRACKET);
            return null;
        }
        /* 缓存逻辑处理 */
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType,
            null, null, null, parameterType, resultMap, resultType,
            null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
            configuration.getDatabaseId(), languageDriver, null);
    }

    /**
     * @since 3.5.0
     */
    protected MappedStatement addMappedStatement(Class<?> mapperClass, SqlSource sqlSource,
                                                 SqlCommandType sqlCommandType, Class<?> parameterType,
                                                 String resultMap, Class<?> resultType, KeyGenerator keyGenerator,
                                                 String keyProperty, String keyColumn) {
        return addMappedStatement(mapperClass, this.methodName, sqlSource, sqlCommandType, parameterType, resultMap, resultType, keyGenerator, keyProperty, keyColumn);
    }

    /**
     * 注入自定义 MappedStatement
     *
     * @param mapperClass mapper 接口
     * @param modelClass  mapper 泛型
     * @param tableInfo   数据库表反射信息
     * @return MappedStatement
     */
    public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);

    /**
     * 获取自定义方法名,未设置采用默认方法名
     * https://gitee.com/baomidou/mybatis-plus/pulls/88
     *
     * @return method
     * @author 义陆无忧
     * @see AbstractMethod#AbstractMethod(java.lang.String)
     * @deprecated 3.5.0
     */
    @Deprecated
    public String getMethod(SqlMethod sqlMethod) {
        return StringUtils.isBlank(methodName) ? sqlMethod.getMethod() : this.methodName;
    }

}

简单看个SelectById类,以这个方法为例子

/**
 * 根据ID 查询一条数据
 *
 * @author hubin
 * @since 2018-04-06
 */
public class SelectById extends AbstractMethod {

    public SelectById() {
    	//给methodName属性赋值
        super(SqlMethod.SELECT_BY_ID.getMethod());
    }

    /**
     * @param name 方法名
     * @since 3.5.0
     */
    public SelectById(String name) {
    //给methodName属性赋值
        super(name);
    }

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    //获取SqlMethod类,这是一个枚举类,下面会有介绍
        SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
        //生成最终的SqlSource对象
        SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
                sqlSelectColumns(tableInfo, false),
                tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
                tableInfo.getLogicDeleteSql(true, true)), Object.class);
         //将最终封装好的MappedStatement对象加入配置类对象中
        return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
    }
}

在这里插入图片描述

断点看看
在这里插入图片描述
可以看到sqlMethod枚举类封装了相关sql语句,短点往下
在这里插入图片描述
最终生成了封装好sql语句和参数的SqlSource对象,这里生成的是StaticSqlSource静态对象

了解完上面的类,现在回头看看初始化时注入方法,不了解的可以先看我的上篇文章。

在这里插入图片描述
来看看parserInjector这个方法
在这里插入图片描述
GlobalConfigUtils.getSqlInjector(configuration)这句代码其实就是返回配置文件中设置的SqlInjector注入器,说明我们可以自定义注入器,默认注入器是DefaultSqlInjector。官方文档也正好证明这一点
在这里插入图片描述
在这里插入图片描述
最终这个方法调用了AbstractSqlInjector的inspectInject方法将CRUD相关的sql对象注入到配置中去

总结

到这里基本就介绍完了。到这里其实就能想到可以通过自定义BaseMapper,并且编写响应method对象,自定义实现ISqlInjector注入器。这样我们就能够对mybatis-plus进行扩展来实现更多的CRUD方法。后面文章我会出一片详细的

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Mybatis-plus是MyBatis增强工具包,用于简化CRUD操作。该工具包为MyBatis提供了一些高效,有用,即用的功能,使用它可以有效地节省您的开发时间。 Mybatis-plus特征: 与MyBatis完全兼容 启动时自动配置 开箱即用的用于操作数据库的界面 强大而灵活的条件包装 生成主键的多种策略 Lambda样式的API 全能和高度可定制的代码生成 自动分页操作 SQL注入防御 支持活动记录 支持可插拔的自定义界面 内置许多有用的扩展 Mybatis-plus功能: 1、单表CURD(简单 + 批量)操作自动完成(支持 like 比较等查询)。 2、分页插件,Count查询自动或自定义SQL查询。 3、Spring根据不同环境加载不同配置支持(支持typeAliasesPackage通配符扫描)。 【自动生成Entity  Mapper  Service文件】 Mybatis-plus更新日志: v3.4.3 增加瀚高数据库支持 增加注解 Order By 支持默认排序 Wrapper exists notExists orderBy groupBy 支持参数绑定 Wrapper 支持 setParamAlias 其它优化 优化 KeyGenerator 支持多实现多数据源注入 增强 ServiceImpl 泛型推断,解决多继承与代理问题 新增 PageDto 用于微服务对象传输序列化 新增 Page 提供静态 of 构造方式 增加代理 MethodHandleProxies 对 lambda 调试支持 调整 ActiveRecord 日志对象初始化 调整 ActiveRecord 模式 Model 类开发 pkVal 方法外部可用 删除标记过时代码 优化枚举值获取方式 分页 count 安全处理 Sequence 方法支持重写支持 升级 Mybatis 3.5.7 修复自动配置 lazy-initialization 无属性提示 修复 mysql on duplicate key update 字段名判断为表名问题 修复 lambda 条件 npe 异常 重构 lambda 信息提取方法 获取 lambda 信息不在序列化 合并 gitee pulls/ 141 fixed github issues/3208 3016 fixed github issues/3482 数据权限处理支持 union all 调整事务未启用打印提示信息 单元测试优化相关依赖升级
MyBatis Plus是一个基于MyBatis的增强工具,它简化了MyBatis的使用,提供了更多的功能和便利性。它通过封装了常用的操作,如CRUD操作、分页查询、乐观锁、逻辑删除等,来简化编码过程并提高开发效率。此外,MyBatis Plus还提供了代码生成,可以根据数据库表生成对应的实体类、Mapper接口和XML映射文件,减少了手动编写这些代码的工作量。 该工具还实现了一些高级特性,如多租户、性能分析、逻辑删除等。同时,MyBatis Plus也保留了MyBatis的灵活性和强大的SQL编写能力,可以与MyBatis无缝集成和共存。 关于MyBatis Plus的源码解析,可以参考【mybatis plus源码解析】系列文章。其中第一篇介绍了MyBatis Plus的执行原理和自动注入CRUD操作实现,第详解SQL注入底层原理自动注入CRUD操作实现。这些文章可以帮助你更深入地理解MyBatis Plus的内部机制和实现原理。 如果你对MyBatis Plus的源码解析感兴趣,可以查看这些文章,它们会对你理解MyBatis Plus的工作原理和使用方式有所帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【mybatis plus源码解析】(一)mybatis plus执行原理,mybatis plus是如何实现自动注入CRUD操作](https://blog.csdn.net/qq_35270805/article/details/123825416)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值