当前位置: 首页 > news >正文

MyBatis-Plus核心内容

MyBatis-Plus

MyBatis-Plus 是一个基于 MyBatis的增强工具,旨在简化开发过程,减少重复代码。它在MyBatis的基础上增加了CRUD操作封装,条件构造器、代码生成器等功能。

一、核心特性与优势

1. 核心特性
  • 无侵入:只做增强不做改变,完全兼容MyBatis
  • 自动CRUD:内置通用Mapper,单表操作无需编写sql
  • 条件构造器:通过Lambda表达式构建复杂查询条件
  • 代码生成器:自动生成Entity、Mapper、Service等代码
  • 分页插件:内置分页功能,支持多种数据库。
  • 乐观锁:内置乐观锁插件,防止脏数据更新。
  • 逻辑删除:支持逻辑删除,避免物理删除数据。
  • 多租户
2. 与原生 MyBatis 对比
功能原生 MyBatisMyBatis-Plus
单表 CRUD手动编写 XML 或注解内置通用 Mapper,零 SQL
条件查询手动拼接 SQL 或 XMLLambda 条件构造器
分页手动实现分页插件内置分页插件,一行代码搞定
代码生成需自定义模板内置代码生成器
性能分析需集成第三方插件内置性能分析插件

二、高级功能

1. 乐观锁插件
// 1. 配置乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件return interceptor;
}// 2. 实体类字段上添加 @Version 注解
@Data
public class Product {private Long id;private String name;@Versionprivate Integer version;
}// 3. 使用
Product product = productService.getById(1L);
product.setPrice(100);
productService.updateById(product); // 自动带上 version 条件
2. 逻辑删除
// 1. 配置逻辑删除插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new LogicSqlInjector()); // 逻辑删除插件return interceptor;
}// 2. 配置 application.yml
mybatis-plus:global-config:db-config:logic-not-delete-value: 0  # 未删除值(默认)logic-delete-value: 1      # 已删除值(默认)// 3. 实体类字段上添加 @TableLogic 注解
@Data
public class User {private Long id;private String name;@TableLogicprivate Integer deleted;
}// 4. 使用
userService.removeById(1L); // 实际执行 UPDATE user SET deleted=1 WHERE id=1 AND deleted=0
3. 性能分析插件
// 配置性能分析插件(开发环境使用)
@Bean
@Profile({"dev","test"}) // 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor interceptor = new PerformanceInterceptor();interceptor.setMaxTime(100); // 最大执行时间,单位毫秒interceptor.setFormat(true); // SQL 是否格式化return interceptor;
}

三、整体架构与核心组件

1. 架构分层
┌───────────────────────────────────────────────────────────┐
│                          用户层                            │
│  (Service/Controller 通过 BaseMapper/IService 调用方法)   │
├───────────────────────────────────────────────────────────┤
│                         接口层                             │
│  (BaseMapper<T>, IService<T>, ServiceImpl<M extends      │
│   BaseMapper<T>, T>)                                       │
├───────────────────────────────────────────────────────────┤
│                         核心层                             │
│  (SqlRunner, SqlHelper, MybatisMapperMethod,              │
│   MybatisMapperProxy)                                      │
├───────────────────────────────────────────────────────────┤
│                        插件层                              │
│  (PaginationInnerInterceptor, OptimisticLockerInnerInterceptor,│
│   LogicSqlInjector)                                        │
├───────────────────────────────────────────────────────────┤
│                        配置层                              │
│  (MybatisPlusAutoConfiguration, MybatisPlusProperties)    │
├───────────────────────────────────────────────────────────┤
│                     MyBatis 原生层                          │
│  (SqlSession, Executor, StatementHandler, ResultSetHandler)│
└───────────────────────────────────────────────────────────┘
2. 核心组件
  • BaseMapper:提供基础 CRUD 方法的接口。
  • ServiceImpl:Service 层的默认实现,封装常用业务逻辑。
  • SqlHelper:SQL 执行工具类,与 MyBatis 交互。
  • MybatisMapperMethod:增强版 Mapper 方法调用处理器。
  • MybatisMapperProxy:Mapper 接口的代理实现。
  • InnerInterceptor:插件机制,用于分页、乐观锁等功能。
3.执行流程总结
用户调用 Service 方法
MP 代理对象拦截
构建 QueryWrapper/LambdaQueryWrapper
生成 SQL 条件
调用 MyBatis SqlSession
Executor 执行查询
插件拦截 分页,乐观锁等
StatementHandler 预处理 SQL
ParameterHandler 设置参数
执行 JDBC 查询
ResultSetHandler 映射结果集
返回结果给用户

四、自动配置原理

1. 自动配置入口
// MybatisPlusAutoConfiguration.java
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {// 创建 SqlSessionFactorySqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(dataSource);// 配置 MyBatis-Plus 专属设置Configuration configuration = new MybatisConfiguration();factory.setConfiguration(configuration);// 注册自定义类型处理器if (!ObjectUtils.isEmpty(this.properties.getTypeHandlersPackage())) {factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());}// 添加插件(分页、乐观锁等)if (!ObjectUtils.isEmpty(this.interceptors)) {factory.setPlugins(this.interceptors.toArray(new Interceptor[0]));}return factory.getObject();}// 其他 Bean 定义...
}
2. 插件注册流程
// MybatisPlusAutoConfiguration.java
@Bean
@ConditionalOnMissingBean
public MybatisPlusInterceptor mybatisPlusInterceptor() {return new MybatisPlusInterceptor();
}// 在需要使用插件的地方注入并配置
@Autowired
private MybatisPlusInterceptor interceptor;// 添加分页插件
@PostConstruct
public void addPageInterceptor() {interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
}

五、通用 CRUD 实现

1. BaseMapper 接口
public interface BaseMapper<T> extends Mapper<T> {int insert(T entity);int deleteById(Serializable id);int deleteByMap(@Param("cm") Map<String, Object> columnMap);int delete(@Param("ew") Wrapper<T> queryWrapper);int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);int updateById(@Param("et") T entity);int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);T selectById(Serializable id);List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);T selectOne(@Param("ew") Wrapper<T> queryWrapper);Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);Page<T> selectPage(Page<T> page, @Param("ew") Wrapper<T> queryWrapper);// 其他方法...
}
2. SQL 注入器(SqlInjector)

MP 通过 SqlInjector 将通用方法对应的 SQL 注入到 MyBatis 中:

// DefaultSqlInjector.java
public class DefaultSqlInjector extends AbstractSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {List<AbstractMethod> methodList = new ArrayList<>();// 添加基础 CRUD 方法methodList.add(new Insert());methodList.add(new Delete());methodList.add(new DeleteByMap());methodList.add(new DeleteById());methodList.add(new DeleteBatchIds());methodList.add(new Update());methodList.add(new UpdateById());methodList.add(new SelectById());methodList.add(new SelectBatchIds());methodList.add(new SelectByMap());methodList.add(new SelectOne());methodList.add(new SelectCount());methodList.add(new SelectList());methodList.add(new SelectPage());return methodList;}
}
3. 方法执行流程
// MybatisMapperMethod.java (关键方法)
public Object execute(SqlSession sqlSession, Object[] args) {Object result;// 根据方法类型执行不同逻辑switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional() &&(result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;
}

六、条件构造器(Wrapper)源码

1. 核心接口与类
// Wrapper 接口定义
public interface Wrapper<T> extends Serializable {String getSqlSegment();Object getEntity();Map<String, Object> getParamNameValuePairs();// 其他方法...
}// AbstractWrapper 实现基本逻辑
public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>>implements Wrapper<T> {protected T entity;protected LinkedHashMap<String, Object> paramNameValuePairs = new LinkedHashMap<>();protected StringBuilder sqlWhere = new StringBuilder();// 条件方法实现public Children eq(R column, Object val) {return addCriterion("=", column, val);}public Children ne(R column, Object val) {return addCriterion("<>", column, val);}public Children gt(R column, Object val) {return addCriterion(">", column, val);}// 其他条件方法...
}// QueryWrapper 具体实现
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>> {// 构造方法和特定方法
}// LambdaQueryWrapper 使用 Lambda 表达式
public class LambdaQueryWrapper<T> extends AbstractWrapper<T, SFunction<T, ?>, LambdaQueryWrapper<T>> {// Lambda 条件方法public <V> LambdaQueryWrapper<T> eq(SFunction<T, V> column, V val) {return super.eq(getColumn(column), val);}// 其他 Lambda 条件方法...
}
2. Lambda 表达式解析

MP 通过 com.baomidou.mybatisplus.core.toolkit.support.SFunctionLambdaUtils 解析 Lambda 表达式:

// LambdaUtils.java (关键方法)
public static <T> String getColumn(SFunction<T, ?> fn) {// 解析 Lambda 表达式获取字段名LambdaMeta meta = LambdaUtils.extract(fn);String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());TableInfo tableInfo = TableInfoHelper.getTableInfo(meta.getInstantiatedType());// 根据字段名获取表列名if (tableInfo != null) {TableFieldInfo fieldInfo = tableInfo.getFieldList().stream().filter(f -> f.getProperty().equals(fieldName)).findFirst().orElse(null);if (fieldInfo != null) {return fieldInfo.getColumn();}}// 如果找不到,使用默认映射规则return StringUtils.camelToUnderline(fieldName);
}

七、分页插件实现

1. 核心拦截器
// PaginationInnerInterceptor.java
public class PaginationInnerInterceptor implements InnerInterceptor {@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {// 获取分页参数IPage<?> page = findPage(parameter).orElse(null);if (page == null || page.getSize() < 0) {return;}// 获取数据库类型DbType dbType = this.dbType;if (dbType == null) {JdbcConnection connection = executor.getTransaction().getConnection();dbType = JdbcUtils.getDbType(connection.getMetaData().getURL());}// 根据不同数据库类型生成不同的分页 SQLDialect dialect = DialectFactory.getDialect(dbType);String buildSql = concatOrderBy(boundSql.getSql(), page);String pageSql = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());// 修改原始 SQLMetaObject metaObject = SystemMetaObject.forObject(boundSql);metaObject.setValue("sql", pageSql);}// 其他方法...
}
2. 方言实现
// MySQLDialect.java
public class MySQLDialect implements IDialect {@Overridepublic String buildPaginationSql(String originalSql, long offset, long limit) {StringBuilder sql = new StringBuilder(originalSql);sql.append(" LIMIT ").append(offset).append(",").append(limit);return sql.toString();}
}// OracleDialect.java
public class OracleDialect implements IDialect {@Overridepublic String buildPaginationSql(String originalSql, long offset, long limit) {StringBuilder sql = new StringBuilder();sql.append("SELECT * FROM (SELECT TMP.*, ROWNUM RN FROM (");sql.append(originalSql);sql.append(") TMP WHERE ROWNUM <= ").append(offset + limit);sql.append(") WHERE RN > ").append(offset);return sql.toString();}
}

八、面试题

1. 乐观锁和逻辑删除的实现原理是什么?
  • 乐观锁

    • 原理:通过版本号(@Version 注解)实现,更新时检查版本号是否一致。
    • 流程:查询时获取版本号 → 更新时带上版本号条件 → 成功后版本号 + 1。
  • 逻辑删除

    • 原理:通过 @TableLogic 注解标记字段,将 DELETE 转换为 UPDATE。
    • 配置:设置 logic-not-delete-value=0logic-delete-value=1
2. 如何自定义 MyBatis-Plus 的插件?
  1. 实现 InnerInterceptor 接口。
  2. 重写 beforeQuerybeforeUpdate 等方法,在 SQL 执行前后进行拦截。
  3. 将插件注册到 MybatisPlusInterceptor 中。
3. MyBatis 的核心组件有哪些?各自的职责是什么?
  • SqlSessionFactory:创建 SqlSession 的工厂,通过 SqlSessionFactoryBuilder 构建。
  • SqlSession:数据库操作的核心接口,提供执行 SQL 的方法。
  • Executor:SQL 执行器,负责处理缓存、事务和 SQL 执行。
  • StatementHandler:处理 SQL 语句的预编译和参数设置。
  • ParameterHandler:处理 SQL 参数。
  • ResultSetHandler:处理查询结果集,映射到 Java 对象。
4. MyBatis 中 #{} 和 ${} 的区别是什么?
  • #{}:预编译处理,将参数替换为占位符 ?,调用PreparedStatement 的set 方法来赋值,防止 SQL 注入。

  • ${}:字符串替换,直接将参数插入 SQL,存在 SQL 注入风险。

  • 示例

    // #{} 生成:SELECT * FROM user WHERE name = ?
    @Select("SELECT * FROM user WHERE name = #{name}")// ${} 生成:SELECT * FROM user WHERE name = '张三'
    @Select("SELECT * FROM user WHERE name = '${name}'")
    
5. MyBatis 的缓存机制是怎样的?
  • 一级缓存

    :基于SqlSessionSqlSession的本地缓存,默认开启。

    • 生命周期:与 SqlSession 一致,Session 关闭时缓存清空。
  • 二级缓存

    :全局缓存,基于namespace隔离。

    • 配置方式:在映射文件中添加 标签或使用 @CacheNamespace 注解。
  • 流程:查询时优先从二级缓存获取 → 再从一级缓存获取 → 最后查询数据库。

6. 如何配置 MyBatis 的二级缓存?

(1) MyBatis 的缓存分为一级缓存和 二级缓存。

一级缓存是 SqlSession 级别的缓存,默认开启。

二级缓存是 NameSpace 级别(Mapper)的缓存,多个 SqlSession 可以共享,使用时需要进行配置开启。

(2) 缓存的查找顺序:二级缓存 => 一级缓存 => 数据库

  1. mybatis-config.xml中启用二级缓存:

    <settings><setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在映射文件中配置缓存:

    <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
    
  3. 实体类实现 Serializable 接口。

7. MyBatis 的插件机制是如何工作的?

参考答案

  • 基于 责任链模式动态代理
  • 插件可以拦截 ExecutorStatementHandlerParameterHandlerResultSetHandler 的方法。
  • 自定义插件需实现 Interceptor 接口,使用 @Intercepts@Signature 注解指定拦截点。
8.MyBatis如何获取自动生成的(主)键值

在标签中使用 useGeneratedKeys 和 keyProperty 两个属性来获取自动生成的主键值。

<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
insert into names (name) values (#{name})
</insert>
9.简述Mybatis的动态SQL,列出常用的6个标签及作用
  • Mybatis 动态sql可以让我们在xml映射文件中,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。

  • Mybatis 提供了9中动态sql标签:

    : 进行条件的判断

    :在判断后的 SQL 语句前面添加 WHERE 关键字,并处理 SQL 语句开始位置的 AND 或者 OR 的问题

    :可以在 SQL 语句前后进行添加指定字符 或者去掉指定字符.

    : 主要用于修改操作时出现的逗号问题

    :类似于 java 中的 switch 语句.在所有的条件中选择其一

    :迭代操作

  • 其执行原理:使用OGNL 从sql参数对象中计算表达式的值,更具表达式的值动态拼接sql,以此来完成动态sql功能

10.Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?

不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。原因就是 namespace+id 是作为 Map<String, MappedStatement>的 key 使用的,如果没有namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。

11.Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么?

Mybatis 有三种基本的 Executor 执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

1)SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。

2)ReuseExecutor:执行 update 或 select,以 sql 作为
key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map

3)BatchExecutor:完成批处理。

12.Mybatis 中如何指定使用哪一种 Executor 执行器?

在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。

http://www.dtcms.com/a/328420.html

相关文章:

  • AAC音频编码器技术详解:原理、应用与发展
  • Java数组排序
  • 嵌入式系统分层开发:架构模式与工程实践(四)(状态机的应用和面向对象的编程)
  • redis认识缓存击穿
  • 特征工程--机器学习
  • [ 数据结构 ] 时间和空间复杂度
  • Linux中Apache与Web之虚拟主机配置指南
  • 栈和队列:数据结构中的基础与应用​
  • GaussDB 数据库架构师修炼(十三)安全管理(2)-数据库权限管理
  • 专题:2025城市NOA智能驾驶研究报告|附70+份报告PDF 汇总下载
  • Spring MVC 处理请求的完整流程详解
  • Kubernetes1.28-单Master集群部署
  • 【Vue中key属性的技术分析】
  • 智能装配线cad【8张】三维图+设计说明书
  • 安卓Fragmnet的生命周期
  • 【5】Transformers快速入门:Transformer 是啥?
  • 【接口自动化】-11-接口加密签名 全局设置封装
  • Android领域驱动设计与分层架构实践
  • TF-IDF:信息检索与文本挖掘的统计权重基石
  • 开源生态认证体系介绍
  • 当 GitHub 宕机时,我们如何协作?
  • 机器学习-集成学习(EnsembleLearning)
  • Linux 可执行程序核心知识笔记:ELF、加载、虚拟地址与动态库
  • MLOps(机器学习运维)LLMOps(大语言模型运维)介绍(通过自动化、标准化和协作优化模型的开发、部署、监控和维护流程)
  • Ubuntu与Rocky系统安装Java全指南
  • 【门诊进销存出入库管理系统】佳易王医疗器械零售进销存软件:门诊进销存怎么操作?系统实操教程 #医药系统进销存
  • 湖北手机基站数据分享
  • 当“超级高速“遇见“智能大脑“:5G-A×AI如何重塑万物智联时代
  • 双椒派E2000D开发板Linux环境配置指南
  • WireShark:非常好用的网络抓包工具