MyBatis 和 MyBatis-Plus对比

MyBatis 和 MyBatis-Plus 是 Java 生态中常用的持久层框架,其中 MyBatis-Plus 是在 MyBatis 基础上的增强工具("只做增强,不做改变")。下面从语法详解优劣对比两部分展开说明。

一、MyBatis 语法详解

MyBatis 的核心是 "SQL 映射",通过 XML 或注解将 Java 方法与 SQL 语句绑定,需手动定义 SQL 逻辑。其语法围绕 "配置文件 + Mapper 接口 + SQL 映射(XML / 注解)" 展开。

1. 核心配置文件(mybatis-config.xml)

用于全局配置 MyBatis,包括数据源、别名、插件、映射器等,示例如下:

复制代码
<?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>
    <!-- 1. 环境配置(数据源) -->
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 2. 别名配置(简化类名引用) -->
    <typeAliases>
        <package name="com.example.entity"/> <!-- 扫描包,别名默认为类名(首字母小写) -->
    </typeAliases>

    <!-- 3. 映射器(关联Mapper接口与SQL映射文件) -->
    <mappers>
        <package name="com.example.mapper"/> <!-- 扫描Mapper接口所在包 -->
    </mappers>
</configuration>
2. Mapper 接口

定义数据操作方法,需与 SQL 映射(XML / 注解)绑定,示例如下:

复制代码
public interface UserMapper {
    // 根据ID查询用户
    User selectById(Long id);
    
    // 新增用户
    int insert(User user);
    
    // 批量删除用户
    int deleteBatch(@Param("ids") List<Long> ids);
}
3. SQL 映射(XML 方式,核心)

通过 XML 标签定义 SQL,支持动态 SQL(MyBatis 一大特色),示例如下(UserMapper.xml):

复制代码
<?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">
<!-- namespace绑定Mapper接口 -->
<mapper namespace="com.example.mapper.UserMapper">

    <!-- 1. 基础查询:根据ID查用户 -->
    <select id="selectById" resultType="User"> <!-- id对应接口方法名,resultType对应返回类型(别名) -->
        SELECT id, username, age, create_time FROM user WHERE id = #{id} <!-- #{id}是参数占位符(预编译,防SQL注入) -->
    </select>

    <!-- 2. 新增用户 -->
    <insert id="insert" parameterType="User"> <!-- parameterType是参数类型 -->
        INSERT INTO user(username, age, create_time) 
        VALUES(#{username}, #{age}, #{createTime}) <!-- #{属性名}对应实体类字段(需与数据库字段映射) -->
    </insert>

    <!-- 3. 动态SQL:批量删除(if+foreach) -->
    <delete id="deleteBatch">
        DELETE FROM user
        <where> <!-- where标签自动处理多余的AND/OR -->
            <if test="ids != null and ids.size() > 0"> <!-- if标签:条件判断(test里是OGNL表达式) -->
                id IN 
                <foreach collection="ids" item="id" open="(" close=")" separator=","> <!-- foreach遍历集合 -->
                    #{id}
                </foreach>
            </if>
        </where>
    </delete>

    <!-- 4. 动态SQL:复杂查询(choose/when/otherwise) -->
    <select id="selectByCondition" resultType="User">
        SELECT * FROM user
        <where>
            <choose> <!-- choose类似Java的switch:只执行第一个满足条件的when -->
                <when test="username != null">username LIKE CONCAT('%', #{username}, '%')</when>
                <when test="age != null">age = #{age}</when>
                <otherwise>1=1</otherwise> <!-- 默认条件(避免where后无内容) -->
            </choose>
        </where>
    </select>
</mapper>
4. SQL 映射(注解方式,适合简单 SQL)

直接在 Mapper 接口方法上用注解定义 SQL,无需 XML,示例如下:

复制代码
public interface UserMapper {
    @Select("SELECT id, username FROM user WHERE id = #{id}")
    User selectById(Long id);

    @Insert("INSERT INTO user(username, age) VALUES(#{username}, #{age})")
    int insert(User user);
}

二、MyBatis-Plus 语法详解

MyBatis-Plus(简称 "MP")基于 MyBatis 开发,核心是 "增强 CRUD"------ 通过继承接口、条件构造器等简化开发,无需手动写基本 SQL,同时兼容 MyBatis 语法(可混合使用)。

1. 核心依赖与配置

需引入 MyBatis-Plus 依赖(需排除 MyBatis 重复依赖),配置与 MyBatis 类似,仅需新增 MP 专属插件(如分页插件),示例如下(Spring Boot 环境):

复制代码
@Configuration
@MapperScan("com.example.mapper") // 扫描Mapper接口
public class MyBatisPlusConfig {
    // 分页插件(MP提供,开箱即用)
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 适配MySQL
        return interceptor;
    }
}
2. 核心接口:BaseMapper<T>(简化 CRUD)

MP 提供 BaseMapper<T> 接口,内置常用 CRUD 方法,自定义 Mapper 只需继承它即可,无需手动写 SQL。

示例

复制代码
// 实体类(需与数据库表映射,用MP注解)
@Data
@TableName("user") // 指定数据库表名(默认类名小写)
public class User {
    @TableId(type = IdType.AUTO) // 主键策略(自增)
    private Long id;
    private String username;
    private Integer age;
    @TableField("create_time") // 映射数据库字段(若属性名与字段名一致可省略)
    private LocalDateTime createTime;
}

// Mapper接口:继承BaseMapper<T>,无需写方法
public interface UserMapper extends BaseMapper<User> { 
    // 已继承BaseMapper的20+个方法:insert、selectById、updateById、deleteById、selectList等
}

直接调用继承的方法(Service 层示例):

复制代码
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void testCRUD() {
        // 1. 新增
        User user = new User();
        user.setUsername("mp-test");
        user.setAge(20);
        userMapper.insert(user); // 直接调用BaseMapper的insert方法

        // 2. 根据ID查询
        User user = userMapper.selectById(1L);

        // 3. 更新(根据ID)
        user.setAge(21);
        userMapper.updateById(user); // 仅更新非null字段

        // 4. 删除(根据ID)
        userMapper.deleteById(1L);

        // 5. 查询所有
        List<User> users = userMapper.selectList(null); // 参数为条件构造器,null表示无条件
    }
}
3. 条件构造器:QueryWrapper/UpdateWrapper(动态 SQL 简化)

MP 提供 QueryWrapper(查询条件)和 UpdateWrapper(更新条件),通过链式方法动态构建条件,无需写 XML 动态 SQL 标签。

示例 1:QueryWrapper(查询条件)

复制代码
public List<User> selectByCondition() {
    // 构建查询条件:查询年龄>20、用户名含"张"、且创建时间在2023年后的用户
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper
        .gt("age", 20) // gt:大于(字段名用数据库列名,或实体类属性名)
        .like("username", "张") // like:模糊查询(默认%值%)
        .ge("create_time", LocalDateTime.of(2023, 1, 1, 0, 0, 0)) // ge:大于等于
        .orderByDesc("create_time"); // 排序:按创建时间降序

    // 调用selectList,传入条件构造器
    return userMapper.selectList(queryWrapper);
}

示例 2:UpdateWrapper(更新条件)

复制代码
public void updateByCondition() {
    // 构建更新条件:将年龄<18的用户的username改为"未成年"
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper
        .lt("age", 18) // lt:小于(条件)
        .set("username", "未成年"); // set:更新的字段及值

    // 调用update,传入null(无需实体类)和条件构造器
    userMapper.update(null, updateWrapper);
}
4. 分页查询(开箱即用)

MP 分页插件配置后,直接用 IPage 接口即可实现分页,无需手动写 LIMIT 语句:

复制代码
public IPage<User> selectPage(int pageNum, int pageSize) {
    // 1. 创建分页对象(pageNum:页码,pageSize:每页条数)
    IPage<User> page = new Page<>(pageNum, pageSize);

    // 2. 调用selectPage方法(BaseMapper提供),传入分页对象和条件构造器(null表示查所有)
    return userMapper.selectPage(page, null); 
    // 返回结果包含:总条数(total)、总页数(pages)、当前页数据(records)等
}
5. 其他增强功能(语法涉及注解 / 配置)
  • 逻辑删除 :通过 @TableLogic 注解标记逻辑删除字段(如 deleted),删除时自动改为 "更新 deleted=1",查询时自动过滤 deleted=0

    复制代码
    @TableLogic // 标记逻辑删除字段
    private Integer deleted;
  • 自动填充 :通过 @TableField(fill = ...) 标记需自动填充的字段(如 createTime),配合 MetaObjectHandler 实现新增 / 更新时自动赋值:

    复制代码
    @TableField(fill = FieldFill.INSERT) // 新增时自动填充
    private LocalDateTime createTime;
    
    // 自定义填充处理器
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        }
    }
  • 代码生成器 :通过 AutoGenerator 自动生成 Entity、Mapper、Service、Controller 等代码,减少重复工作:

    复制代码
    public class CodeGenerator {
        public static void main(String[] args) {
            AutoGenerator generator = new AutoGenerator();
            // 配置数据源、包名、策略等(略)
            generator.execute(); // 执行生成
        }
    }

三、MyBatis 与 MyBatis-Plus 的优劣对比

两者核心差异在于 "开发效率" 与 "灵活性" 的权衡,具体对比如下:

1. MyBatis 的优劣

优势

  • 灵活性极高:开发者可完全控制 SQL 语句,支持复杂查询(如多表联查、子查询、自定义函数等),适合业务逻辑复杂的场景(如金融、电商核心业务)。
  • SQL 优化可控:可直接通过调整 SQL 语句优化性能(如索引利用、查询字段精简),对底层 SQL 透明。
  • 学习成本低:核心逻辑是 "SQL 映射",只需掌握 XML / 注解与 SQL 的绑定,概念简单清晰。
  • 无额外依赖:轻量框架,仅需 MyBatis 核心包,无多余封装,适合对依赖体积敏感的项目。

劣势

  • 重复代码多:基本 CRUD(增删改查)需手动写 SQL(即使逻辑简单),Mapper 接口、XML 映射文件需一一对应,开发效率低。
  • 动态 SQL 繁琐 :复杂动态 SQL 需写大量 XML 标签(如 if/foreach/where),易出错且维护困难(如标签嵌套、语法错误)。
  • 功能需手动扩展 :分页、逻辑删除等功能需自己实现(如自定义分页插件、手动加 deleted 判断),无开箱即用的解决方案。
2. MyBatis-Plus 的优劣

优势

  • 开发效率极高 :继承 BaseMapper 即可实现 80% 以上的基础 CRUD,无需写 SQL;条件构造器简化动态 SQL,无需 XML 标签。
  • 功能丰富且开箱即用:内置分页、逻辑删除、自动填充、代码生成器等功能,无需手动开发,适合快速迭代的项目(如后台管理系统、中小业务)。
  • 兼容 MyBatis:可无缝混合使用 MyBatis 语法(如复杂查询仍用 XML 写 SQL),既保留灵活性又提升效率。
  • 降低新手门槛:无需熟练掌握 XML 动态 SQL,通过条件构造器的链式方法即可完成大部分查询,新手易上手。

劣势

  • 复杂查询灵活性不足:虽然支持自定义 SQL,但过度依赖条件构造器可能导致复杂查询(如多表联查 + 动态条件)写法繁琐,不如直接写 SQL 直观。
  • 框架依赖增加:需引入 MyBatis-Plus 依赖,且部分功能(如代码生成器)需额外配置,对 "轻量" 有要求的项目可能不适用。
  • 底层 SQL 透明性低:条件构造器生成的 SQL 需通过日志查看,若对 MP API 不熟悉,可能出现 "预期外 SQL"(如条件拼接错误),排查问题成本高。
  • 团队学习成本:需掌握 MP 专属 API(如条件构造器方法、注解),若团队成员不熟悉,可能出现用法不统一的问题。

四、总结:如何选择?

  • MyBatis:若项目业务复杂(多复杂查询)、对 SQL 可控性要求高(需频繁优化),或团队更习惯直接写 SQL。
  • MyBatis-Plus:若项目以基础 CRUD 为主(如后台管理系统)、追求快速开发,或需减少重复代码(如自动填充、分页),可大幅提升效率。

实际开发中,两者常结合使用:基础 CRUD 用 MP 简化开发,复杂查询用 MyBatis 的 XML 写原生 SQL,兼顾效率与灵活性。

相关推荐
羚羊角uou38 分钟前
【Linux】POSIX信号量、环形队列、基于环形队列实现生产者消费者模型
java·开发语言
你是狒狒吗2 小时前
为什么mysql要有主从复制,主库,从库这种东西
数据库·mysql
倔强的石头1065 小时前
【金仓数据库】ksql 指南(一) 连接本地 KingbaseES 数据库与基础交互
数据库·oracle·kingbasees·金仓数据库·ksql
代码萌新知7 小时前
设计模式学习(五)装饰者模式、桥接模式、外观模式
java·学习·设计模式·桥接模式·装饰器模式·外观模式
iナナ9 小时前
Spring Web MVC入门
java·前端·网络·后端·spring·mvc
驱动探索者9 小时前
find 命令使用介绍
java·linux·运维·服务器·前端·学习·microsoft
卷Java9 小时前
违规通知功能修改说明
java·数据库·微信小程序·uni-app
CoderYanger10 小时前
优选算法-双指针:2.复写零
java·后端·算法·leetcode·职场和发展
小雨凉如水10 小时前
k8s学习-pod的生命周期
java·学习·kubernetes
李宥小哥10 小时前
C#基础10-结构体和枚举
java·开发语言·c#