MyBatis 深度解析
核心组件
-
SqlSessionFactory
- 作用:创建 SqlSession 对象,是 MyBatis 的核心入口
- 配置方式:
- XML 配置(mybatis-config.xml)
- Java 代码构建(较少使用)
-
SqlSession
- 作用:执行 SQL 命令、管理事务
- 特点:非线程安全,建议每次使用时创建新实例
-
Executor
- 作用:执行 SQL 语句,处理缓存
- 类型:
- SimpleExecutor:每次执行都会创建新的 Statement
- ReuseExecutor:重复使用 Statement
- BatchExecutor:批量执行 SQL
-
StatementHandler
- 作用:处理 SQL 语句的预编译、参数设置、结果集映射
配置细节
-
全局配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers> </configuration>
-
映射文件结构
<mapper namespace="com.example.mapper.UserMapper"> <!-- 结果映射 --> <resultMap id="BaseResultMap" type="com.example.entity.User"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="age" property="age" jdbcType="INTEGER"/> </resultMap> <!-- 查询语句 --> <select id="selectById" resultMap="BaseResultMap"> SELECT * FROM user WHERE id = #{id} </select> <!-- 插入语句 --> <insert id="insert" parameterType="com.example.entity.User"> INSERT INTO user (name, age) VALUES (#{name}, #{age}) </insert> </mapper>
高级特性
-
动态 SQL
- 标签:
<if>
,<choose>
,<when>
,<otherwise>
,<where>
,<set>
,<foreach>
- 示例:
<select id="findUsers" resultType="User"> SELECT * FROM user <where> <if test="name != null"> AND name = #{name} </if> <if test="age != null"> AND age = #{age} </if> </where> </select>
- 标签:
-
缓存机制
- 一级缓存:SqlSession 级别
- 二级缓存:全局级别,跨 SqlSession 共享
- 配置方式:
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
-
插件机制
- 可拦截的方法:Executor、ParameterHandler、ResultSetHandler、StatementHandler
- 示例:
@Intercepts({@Signature( type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} )}) public class ExamplePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 增强逻辑 return invocation.proceed(); } }
MyBatis-Plus 深度解析
核心组件
-
BaseMapper
- 内置方法:
int insert(T entity); int deleteById(Serializable id); int updateById(@Param(Constants.ENTITY) T entity); T selectById(Serializable id); List<T> selectList(Wrapper<T> queryWrapper);
- 内置方法:
-
IService
- 内置方法:
boolean save(T entity); boolean removeById(Serializable id); boolean updateById(T entity); T getById(Serializable id); List<T> list(Wrapper<T> queryWrapper);
- 内置方法:
-
ServiceImpl
- 实现 IService 接口,提供基础 CRUD 功能
- 使用方式:
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 可扩展自定义方法 }
增强特性
-
条件构造器
- Lambda 表达式方式:
List<User> userList = userService.list( new LambdaQueryWrapper<User>() .eq(User::getName, "张三") .ge(User::getAge, 18) .orderByDesc(User::getCreateTime) );
- Lambda 表达式方式:
-
自动填充
- 实现 MetaObjectHandler 接口:
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }
- 实现 MetaObjectHandler 接口:
-
逻辑删除
- 实体类字段添加注解:
@TableLogic private Integer deleted;
- 实体类字段添加注解:
-
分页插件
- 配置类:
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
- 配置类:
-
代码生成器
- 使用示例:
FastAutoGenerator.create("jdbc:mysql://localhost:3306/test", "root", "password") .globalConfig(builder -> { builder.author("baomidou") // 设置作者 .outputDir("D://mybatis-plus-code"); // 指定输出目录 }) .packageConfig(builder -> { builder.parent("com.example") // 设置父包名 .moduleName("module") // 设置模块名 .pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatis-plus-code/xml")); // 设置mapperXml生成路径 }) .strategyConfig(builder -> { builder.addInclude("user") // 设置需要生成的表名 .addTablePrefix("t_", "c_"); // 设置过滤表前缀 }) .execute();
- 使用示例:
性能对比
场景 | MyBatis 执行时间 | MyBatis-Plus 执行时间 | 原因分析 |
---|---|---|---|
单表查询(无复杂条件) | 约 20ms | 约 25ms | MP 有额外的反射和代理开销 |
单表分页查询 | 约 30ms | 约 35ms | MP 分页插件有一定开销 |
复杂多表联查 | 约 50ms | 约 55ms | MP 优势不明显,需手写 SQL |
批量插入 1000 条数据 | 约 800ms | 约 600ms | MP 内置批量插入优化 |
最佳实践建议
-
项目选型
- 新项目建议优先考虑 MyBatis-Plus,开发效率更高
- 遗留项目已有大量 MyBatis 代码,可逐步引入 MyBatis-Plus
-
分层设计
- Controller → Service → Mapper
- Service 层使用 MyBatis-Plus 的 IService 和 ServiceImpl
- 复杂查询在 Mapper 层自定义 SQL
-
复杂查询处理
- 方式一:在 Mapper 接口中定义方法,XML 中编写 SQL
- 方式二:使用 MyBatis-Plus 的 Wrapper 构造复杂条件
- 方式三:结合 MyBatis-Plus 的 QueryWrapper 和自定义 SQL
-
分页处理
- 单表分页:直接使用 MyBatis-Plus 分页插件
- 复杂分页:自定义 SQL + 分页插件
-
事务管理
- 使用 Spring 的 @Transactional 注解管理事务
- 示例:
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Override @Transactional(rollbackFor = Exception.class) public boolean saveUserAndLog(User user) { // 保存用户 boolean result = this.save(user); // 记录日志 logService.saveLog("用户注册:" + user.getName()); return result; } }
常见问题与解决方案
-
MyBatis 问题
- 问题:SQL 注入风险
- 解决方案:使用 #{} 占位符,避免使用 ${}
-
MyBatis-Plus 问题
- 问题:复杂查询性能不如纯手写 SQL
- 解决方案:复杂查询部分使用自定义 SQL
-
通用问题
- 问题:字段映射不一致
- 解决方案:
- 使用 @TableField 注解
- 配置全局下划线转驼峰
-
分页问题
- 问题:分页插件不生效
- 解决方案:
- 检查分页插件是否正确配置
- 确保调用的是 Page 相关方法
-
缓存问题
- 问题:二级缓存导致数据不一致
- 解决方案:
- 敏感数据禁用二级缓存
- 更新数据时及时清除缓存
通过以上对比可以看出,MyBatis-Plus 在保持 MyBatis 核心功能的基础上,显著提升了开发效率,尤其适合以 CRUD 操作为主的项目。但在复杂 SQL 场景下,仍需要结合 MyBatis 的原生能力。