1. 引言
在 Java 后端开发中,持久层框架的选择直接影响开发效率与代码可维护性。MyBatis 以「SQL 可控、映射灵活」著称,而 MyBatis-Plus 在 MyBatis 之上做了增强,提供了大量开箱即用的 CRUD 能力。
本文从零开始,分别演示 原生 MyBatis 与 MyBatis-Plus 的详细用法,并在最后给出两者的对比与选型建议。全文基于以下版本:
| 组件 | 版本 |
|---|---|
| JDK | 17 |
| Spring Boot | 3.2.x |
| MyBatis Spring Boot Starter | 3.0.3 |
| MyBatis-Plus Spring Boot3 Starter | 3.5.5 |
| MySQL | 8.0 |
2. 环境准备
2.1 建表语句
先准备一张用户表,后续两种方案都基于它演示:
CREATE TABLE `sys_user` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` VARCHAR(64) NOT NULL COMMENT '用户名',
`password` VARCHAR(128) NOT NULL COMMENT '密码',
`age` INT DEFAULT NULL COMMENT '年龄',
`email` VARCHAR(128) DEFAULT NULL COMMENT '邮箱',
`deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除 1-已删除',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
2.2 公共配置
application.yml 中的数据库连接配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
3. 原生 MyBatis 详细用法
3.1 引入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
3.2 实体类
package com.example.demo.entity;
import java.time.LocalDateTime;
public class SysUser {
private Long id;
private String username;
private String password;
private Integer age;
private String email;
private Integer deleted;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 省略 getter / setter,实际项目可用 Lombok 的 @Data
}
3.3 Mapper 接口
MyBatis 的 Mapper 接口只声明方法,SQL 写在 XML 或注解中:
package com.example.demo.mapper;
import com.example.demo.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysUserMapper {
int insert(SysUser user);
int updateById(SysUser user);
int deleteById(@Param("id") Long id);
SysUser selectById(@Param("id") Long id);
List<SysUser> selectList(@Param("username") String username,
@Param("minAge") Integer minAge);
}
3.4 XML 映射文件
在 src/main/resources/mapper/SysUserMapper.xml 中编写 SQL:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.SysUserMapper">
<resultMap id="BaseResultMap" type="com.example.demo.entity.SysUser">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="age" property="age"/>
<result column="email" property="email"/>
<result column="deleted" property="deleted"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<sql id="Base_Column_List">
id, username, password, age, email, deleted, create_time, update_time
</sql>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO sys_user (username, password, age, email)
VALUES (#{username}, #{password}, #{age}, #{email})
</insert>
<update id="updateById">
UPDATE sys_user
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="age != null">age = #{age},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>
<delete id="deleteById">
DELETE FROM sys_user WHERE id = #{id}
</delete>
<select id="selectById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM sys_user
WHERE id = #{id}
</select>
<select id="selectList" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM sys_user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
</where>
ORDER BY id DESC
</select>
</mapper>
同时在 application.yml 中指定映射文件位置:
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.entity
configuration:
map-underscore-to-camel-case: true
3.5 注解方式(简单 SQL 可选)
对于简单 SQL,也可以直接用注解,省去 XML:
@Mapper
public interface SysUserAnnotationMapper {
@Select("SELECT * FROM sys_user WHERE id = #{id}")
SysUser selectById(@Param("id") Long id);
@Insert("INSERT INTO sys_user(username, password, age) VALUES(#{username}, #{password}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(SysUser user);
}
3.6 动态 SQL 常用标签
MyBatis 的强大之处在于动态 SQL,常用标签如下:
<if>:条件判断<where>:自动处理AND/OR前缀<set>:自动处理更新语句末尾逗号<foreach>:遍历集合,常用于IN查询<choose>/<when>/<otherwise>:多分支选择
<foreach> 示例:
<select id="selectByIds" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM sys_user
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
3.7 事务与使用示例
@Service
public class UserService {
private final SysUserMapper userMapper;
public UserService(SysUserMapper userMapper) {
this.userMapper = userMapper;
}
@Transactional(rollbackFor = Exception.class)
public SysUser createUser(SysUser user) {
userMapper.insert(user);
return user;
}
public List<SysUser> search(String username, Integer minAge) {
return userMapper.selectList(username, minAge);
}
}
4. MyBatis-Plus 详细用法
4.1 引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
注意:Spring Boot 3 必须使用
mybatis-plus-spring-boot3-starter,Spring Boot 2 使用mybatis-plus-boot-starter。
4.2 实体类与注解
MyBatis-Plus 通过注解把实体与表结构关联起来:
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_user")
public class SysUser {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private Integer age;
private String email;
@TableLogic
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
常用注解说明:
| 注解 | 作用 |
|---|---|
@TableName | 指定表名 |
@TableId | 指定主键及生成策略(AUTO/ASSIGN_ID/INPUT 等) |
@TableField | 指定字段名、填充策略、是否参与查询等 |
@TableLogic | 标记逻辑删除字段 |
@Version | 乐观锁版本号字段 |
4.3 Mapper 接口
只需继承 BaseMapper<T>,即可获得一整套 CRUD 方法:
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
// 无需写任何方法即可使用 insert/deleteById/updateById/selectById/selectList 等
}
4.4 基础 CRUD 用法
@Service
public class UserService {
private final SysUserMapper userMapper;
public UserService(SysUserMapper userMapper) {
this.userMapper = userMapper;
}
/** 新增:自动回填主键 */
public SysUser save(SysUser user) {
userMapper.insert(user);
return user;
}
/** 按主键删除(配置 @TableLogic 后为逻辑删除) */
public void remove(Long id) {
userMapper.deleteById(id);
}
/** 按主键更新:只更新非 null 字段 */
public void modify(SysUser user) {
userMapper.updateById(user);
}
/** 按主键查询 */
public SysUser getById(Long id) {
return userMapper.selectById(id);
}
/** 条件查询列表 */
public List<SysUser> listByAge(Integer minAge) {
return userMapper.selectList(
new LambdaQueryWrapper<SysUser>()
.ge(SysUser::getAge, minAge)
.orderByDesc(SysUser::getId)
);
}
}
4.5 条件构造器 Wrapper
Wrapper 是 MyBatis-Plus 的核心,推荐使用 LambdaQueryWrapper / LambdaUpdateWrapper,避免硬编码字段名:
// 查询:用户名模糊匹配 + 年龄大于等于 18 + 按 id 倒序
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<SysUser>()
.like(StringUtils.hasText(username), SysUser::getUsername, username)
.ge(minAge != null, SysUser::getAge, minAge)
.orderByDesc(SysUser::getId);
List<SysUser> users = userMapper.selectList(query);
// 更新:把年龄小于 18 的用户邮箱置空
LambdaUpdateWrapper<SysUser> update = new LambdaUpdateWrapper<SysUser>()
.lt(SysUser::getAge, 18)
.set(SysUser::getEmail, null);
userMapper.update(null, update);
// 删除:按条件删除
userMapper.delete(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUsername, "test"));
常用条件方法:
| 方法 | 说明 |
|---|---|
eq / ne | 等于 / 不等于 |
gt / ge / lt / le | 大于 / 大于等于 / 小于 / 小于等于 |
like / likeLeft / likeRight | 模糊匹配 |
in / notIn | IN / NOT IN |
isNull / isNotNull | 判空 |
between | 区间 |
orderByAsc / orderByDesc | 排序 |
4.6 分页插件
MyBatis-Plus 的分页需要注册插件:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用分页:
public Page<SysUser> page(int pageNo, int pageSize) {
Page<SysUser> page = new Page<>(pageNo, pageSize);
return userMapper.selectPage(page,
new LambdaQueryWrapper<SysUser>().orderByDesc(SysUser::getId));
}
4.7 自动填充与逻辑删除
自动填充需要实现 MetaObjectHandler:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
逻辑删除只需在实体字段上加 @TableLogic,并在配置中声明(3.5.x 默认已支持):
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
配置后,deleteById 实际执行的是 UPDATE sys_user SET deleted = 1 WHERE id = ?,而所有查询会自动追加 deleted = 0 条件。
4.8 自定义 SQL 与 XML
MyBatis-Plus 并不排斥原生 SQL,复杂查询仍可写 XML:
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
List<SysUser> selectByCondition(@Param("username") String username,
@Param("minAge") Integer minAge);
}
<select id="selectByCondition" resultType="com.example.demo.entity.SysUser">
SELECT * FROM sys_user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
</where>
</select>
5. 两者对比与选型建议
| 维度 | MyBatis | MyBatis-Plus |
|---|---|---|
| 基础 CRUD | 需手写 SQL/XML | 继承 BaseMapper 即可 |
| 条件查询 | 手写动态 SQL | Wrapper 链式调用 |
| 分页 | 需引入 PageHelper 等 | 内置分页插件 |
| 逻辑删除 | 需手动处理 | @TableLogic 自动处理 |
| 自动填充 | 需手动赋值 | MetaObjectHandler 自动填充 |
| SQL 可控性 | 完全可控 | 完全可控(可写 XML) |
| 学习成本 | 较低 | 略高(需理解 Wrapper) |
| 适用场景 | 复杂 SQL 多、SQL 需精细调优 | 单表 CRUD 多、追求开发效率 |
选型建议:
- 业务以单表增删改查为主、追求开发效率 → 优先 MyBatis-Plus。
- 存在大量多表关联、复杂报表 SQL → 用 MyBatis-Plus 的
BaseMapper处理简单场景,复杂 SQL 仍写 XML,两者可共存。 - 团队对 SQL 有严格审计要求 → 原生 MyBatis 更直观,SQL 全部集中在 XML 中便于 review。
6. 总结
MyBatis 提供了灵活可控的 SQL 映射能力,是理解持久层原理的基础;MyBatis-Plus 在其之上补齐了通用 CRUD、条件构造器、分页、逻辑删除、自动填充等能力,大幅减少样板代码。
实际项目中,MyBatis-Plus + 自定义 XML 的组合最为常见:简单查询用 Wrapper 一行搞定,复杂 SQL 用 XML 精确控制,兼顾效率与可控性。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_43565087/article/details/169141138




