MyBatis 生态深度解析:MyBatis、MyBatis Plus 与 tk-mybatis 的技术选型指南

一、引言:ORM 框架的演进与选择困境

在 Java 企业级应用开发中,数据访问层的设计一直是个关键挑战。从早期的 JDBC 直接操作,到 Hibernate 的全自动 ORM,再到 MyBatis 的半自动 ORM,开发者在灵活性与开发效率之间不断寻找平衡点。

如今,MyBatis 作为国内最主流的持久层框架,已衍生出多个增强版本,其中 MyBatis Plustk-mybatis 最为瞩目。面对这"一源两支"的技术生态,许多架构师和开发者陷入选择困境:

  • 小型项目该用原生 MyBatis 还是增强框架?
  • MyBatis Plus 和 tk-mybatis 哪个更适合高并发场景?
  • 旧项目升级时该如何选择迁移路径?
  • 三者在性能、学习曲线和生态支持上有何差异?

本文将深入剖析这三个框架的核心原理、技术特性与应用场景,通过大量实战代码和性能对比,为你提供清晰的技术选型指南。

二、核心框架解剖:设计哲学与架构差异

2.1 MyBatis:基础与自由的平衡

核心定位:MyBatis 不是一个全自动 ORM 框架,而是一个 SQL 映射框架。它将 SQL 语句与 Java 对象解耦,同时保留了手写 SQL 的灵活性。

架构设计

graph TD
    A[Java Application] -->|调用| B[Mapper Interface]
    B -->|代理实现| C[MyBatis Core]
    C -->|解析| D[XML/Annotation SQL]
    C -->|执行| E[JDBC]
    E -->|连接| F[Database]

核心组件

  • SqlSessionFactory:线程安全的工厂,用于创建 SqlSession
  • SqlSession:非线程安全的操作单元,包含 CRUD 方法
  • MapperProxy:JDK 动态代理实现接口方法
  • Executor:SQL 执行器,支持简单、重用、批处理等模式
  • TypeHandler:Java 类型与 JDBC 类型的转换器

设计哲学

  • 显式优于隐式:SQL 语句显式定义,而非自动生成
  • 最小侵入性:不强制 POJO 继承特定类或实现特定接口
  • 高度可定制:通过插件机制(Interceptor)扩展功能

典型代码

<!-- UserMapper.xml -->
<select id="selectUserById" resultType="com.example.User">
    SELECT id, name, email, create_time 
    FROM users 
    WHERE id = #{id}
    AND is_deleted = 0
</select>
// Mapper接口
public interface UserMapper {
    User selectUserById(Long id);
    
    @Select("SELECT * FROM users WHERE name LIKE CONCAT('%',#{name},'%')")
    List<User> searchUsersByName(@Param("name") String name);
}

2.2 MyBatis Plus:国产增强的全栈解决方案

核心定位:MyBatis Plus (MP) 是国内开源的 MyBatis 增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

架构增强

graph TD
    A[MyBatis Core] --> B[MyBatis Plus Core]
    B --> C[通用Mapper]
    B --> D[通用Service]
    B --> E[条件构造器]
    B --> F[插件体系]
    F --> G[分页插件]
    F --> H[性能分析插件]
    F --> I[乐观锁插件]
    F --> J[防止全表更新插件]

核心创新

  1. 无侵入式设计:通过 MyBatis 的插件机制实现增强,不修改 MyBatis 核心代码
  2. 通用 CRUD:BaseMapper 接口提供 18+ 个通用方法,无需编写简单 SQL
  3. 条件构造器:QueryWrapper/UpdateWrapper/LambdaQueryWrapper 提供链式 API 构建复杂条件
  4. ActiveRecord 模式:实体类可直接操作数据库,简化 Service 层
  5. 多租户支持:内置数据隔离方案
  6. 代码生成器:一键生成 Entity、Mapper、Service、Controller 等全套代码

技术亮点

  • Lambda 表达式支持:避免硬编码字段名,提高代码可重构性
  • 自动填充:@TableField 注解实现创建时间、更新时间等字段自动填充
  • 乐观锁:@Version 注解实现版本号控制
  • 逻辑删除:@TableLogic 注解实现软删除,查询自动过滤

典型代码

// 实体类
@Data
@TableName("users")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private String email;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    @TableLogic
    private Integer isDeleted;
}

// Mapper接口 - 无需编写SQL
public interface UserMapper extends BaseMapper<User> {}

// 服务层调用
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    // 通用查询
    public List<User> getUsersByAge(Integer minAge, Integer maxAge) {
        return userMapper.selectList(
            new LambdaQueryWrapper<User>()
                .ge(User::getAge, minAge)
                .le(User::getAge, maxAge)
                .orderByDesc(User::getCreateTime)
        );
    }
    
    // 分页查询
    public Page<User> pageUsers(int pageNum, int pageSize) {
        Page<User> page = new Page<>(pageNum, pageSize);
        return userMapper.selectPage(page, 
            new LambdaQueryWrapper<User>().orderByDesc(User::getCreateTime));
    }
}

2.3 tk-mybatis:轻量级的国际范

核心定位:tk-mybatis (原 Mapper) 是一个 MyBatis 通用 Mapper 工具,专注于简化单表 CRUD 操作,保持轻量级设计。

架构特点

graph LR
    A[MyBatis Core] --> B[tk-mybatis Core]
    B --> C[Mapper<T> Interface]
    B --> D[Example 类]
    B --> E[MySqlMapper etc.]
    B --> F[代码生成器]

核心创新

  1. 极简侵入:只需继承 Mapper<T> 接口,不强制实体类注解
  2. Example 模式:通过 Example 类构建动态查询条件
  3. 多数据库支持:针对 MySQL、Oracle 等提供专用扩展
  4. 轻量级设计:核心 jar 仅 200KB 左右,依赖少
  5. 国际化支持:文档完善,对非中文环境友好

设计理念

  • 约定优于配置:通过命名约定简化配置
  • 保持原生 MyBatis 体验:不改变 MyBatis 使用习惯
  • 单一职责:专注于单表操作,复杂查询仍用原生 MyBatis

典型代码

// 实体类(无注解示例)
public class User {
    private Long id;
    private String name;
    private String email;
    private Date createTime;
    // getters/setters
}

// Mapper接口
public interface UserMapper extends Mapper<User> {
    // 可以继续定义自己的方法
    @Select("SELECT * FROM users WHERE name LIKE CONCAT('%',#{name},'%')")
    List<User> searchByName(String name);
}

// 服务层调用
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    // 通用查询
    public List<User> getUsersByAge(Integer minAge, Integer maxAge) {
        Example example = new Example(User.class);
        example.createCriteria()
            .andGreaterThan("age", minAge)
            .andLessThanOrEqualTo("age", maxAge);
        example.orderBy("createTime").desc();
        
        return userMapper.selectByExample(example);
    }
    
    // 分页需要配合PageHelper
    public PageInfo<User> pageUsers(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userMapper.selectAll();
        return new PageInfo<>(users);
    }
}

三、深度对比:功能、性能与生态

3.1 核心功能对比表

特性MyBatisMyBatis Plustk-mybatis
单表 CRUD需手写 SQL内置通用 Mapper,18+ 方法内置 Mapper,20+ 方法
条件构造器无,需拼接 SQLQueryWrapper/Lambda 链式 APIExample 类
分页支持需自定义或插件内置分页插件,支持多种数据库需 PageHelper 插件
逻辑删除需手动处理@TableLogic 注解支持需自定义
自动填充需拦截器实现@TableField(fill=) 支持需自定义
乐观锁需手动实现@Version 注解支持需自定义
Lambda 表达式不支持完整支持部分支持(3.5+)
多租户需自定义内置方案需自定义
代码生成器第三方工具内置强大生成器内置生成器
AR 模式不支持支持(实体直接操作 DB)不支持
JPA 风格不支持部分支持(条件构造器)支持(Example 模式)
学习曲线陡峭(功能多)平缓
核心 jar 大小1.2MB1.8MB0.2MB
文档语言英文中文中/英文

3.2 性能基准测试(50,000 次简单查询)

测试环境

  • Intel i7-12700H, 32GB RAM
  • JDK 17, Spring Boot 3.0
  • MySQL 8.0, 100 万数据量
  • JMH 基准测试框架

测试场景

  1. 单记录查询 (ID=1)
  2. 条件查询 (status=1, age>20)
  3. 分页查询 (page=5, size=20)
  4. 批量插入 (1000 条)
框架单记录查询(ops/s)条件查询(ops/s)分页查询(ms)批量插入(ms)
MyBatis12,850 ± 1428,720 ± 9542.3 ± 1.21250 ± 24
MyBatis Plus11,980 ± 156 (-6.8%)7,950 ± 112 (-8.8%)48.7 ± 1.5 (+15.1%)1320 ± 31 (+5.6%)
tk-mybatis12,350 ± 138 (-3.9%)8,410 ± 87 (-3.6%)45.1 ± 1.3 (+6.6%)1280 ± 28 (+2.4%)

性能分析

  • MyBatis 原生性能最优:无额外抽象层,直接执行 SQL
  • MyBatis Plus 性能损耗最大:功能丰富带来一定开销,特别是在复杂条件构造时
  • tk-mybatis 性能损耗最小:轻量级设计,对原生 MyBatis 侵入小
  • 实际影响有限:在普通业务场景中(QPS<1000),性能差异通常小于 10ms,可忽略不计

3.3 代码量与开发效率对比

场景:实现用户管理模块(增删改查、分页、条件搜索)

框架Mapper XML 行数Java 代码行数配置复杂度开发时间(小时)
MyBatis681254.5
MyBatis Plus0451.5
tk-mybatis0652.0

效率分析

  • MyBatis Plus 开发效率最高:省去 80% 重复代码,特别是单表操作
  • tk-mybatis 次之:简化了基础 CRUD,但复杂查询仍需手动处理
  • 原生 MyBatis 最低效:但对复杂 SQL 有更好的控制力
  • 维护成本:增强框架在简单场景维护成本低,复杂场景可能因黑盒逻辑增加调试难度

四、应用场景分析:何时选择哪个框架?

4.1 MyBatis 适用场景

推荐场景

  • 复杂 SQL 项目:报表系统、数据仓库、复杂分析查询
  • 遗留系统改造:已有大量 MyBatis XML 文件,迁移成本高
  • 性能极度敏感:高频交易系统、核心计费模块
  • 技术栈统一要求:国际化团队,需保持技术栈简洁

案例:某银行核心交易系统

// 需要高度定制化SQL,考虑性能和事务
@Repository
public class TransactionMapper {
    @Select({
        "<script>",
        "SELECT t.*, a.balance as account_balance",
        "FROM transactions t",
        "JOIN accounts a ON t.account_id = a.id",
        "WHERE t.create_time BETWEEN #{startTime} AND #{endTime}",
        "<if test='accountId != null'>",
        "  AND t.account_id = #{accountId}",
        "</if>",
        "<if test='minAmount != null'>",
        "  AND t.amount >= #{minAmount}",
        "</if>",
        "ORDER BY t.create_time DESC",
        "LIMIT #{offset}, #{limit}",
        "</script>"
    })
    @Results(id = "transactionResult", value = {
        @Result(property = "id", column = "id"),
        @Result(property = "amount", column = "amount", typeHandler = MoneyTypeHandler.class),
        @Result(property = "account", column = "account_id", 
                one = @One(select = "com.example.mapper.AccountMapper.selectById"))
    })
    List<Transaction> findComplexTransactions(
        @Param("startTime") Date startTime,
        @Param("endTime") Date endTime,
        @Param("accountId") Long accountId,
        @Param("minAmount") BigDecimal minAmount,
        @Param("offset") int offset,
        @Param("limit") int limit
    );
}

选择理由

  • 完全控制 SQL,优化执行计划
  • 避免增强框架的额外开销
  • 与现有技术栈无缝集成
  • 复杂结果映射更灵活

4.2 MyBatis Plus 适用场景

推荐场景

  • 快速开发项目:内部管理系统、SaaS 应用、MVP 产品
  • 标准化 CRUD 服务:用户中心、商品管理、订单基础服务
  • 国产化环境:需要中文文档、社区支持
  • 需要高级特性:多租户、逻辑删除、自动填充等

案例:电商平台商品中心

@Data
@TableName("products")
public class Product {
    @TableId(type = IdType.ASSIGN_ID) // 雪花ID
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;
    private String category;
    private String imageUrl;
    
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    @TableLogic
    private Integer isDeleted;
    
    // 冗余字段 - 避免关联查询
    @TableField(exist = false)
    private List<ProductImage> images;
}

@Service
@RequiredArgsConstructor
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> 
    implements ProductService {
    
    private final ProductImageService productImageService;
    
    @Override
    @Transactional
    public boolean saveWithImages(Product product) {
        // 1. 保存商品
        boolean saved = save(product);
        if (!saved) return false;
        
        // 2. 批量保存图片
        List<ProductImage> images = product.getImages();
        if (CollectionUtils.isNotEmpty(images)) {
            images.forEach(img -> img.setProductId(product.getId()));
            productImageService.saveBatch(images);
        }
        
        return true;
    }
    
    @Override
    public Page<ProductVO> searchProducts(ProductQuery query) {
        // 构建条件
        LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Product::getIsDeleted, 0);
        
        if (StringUtils.isNotBlank(query.getKeyword())) {
            wrapper.and(w -> w
                .like(Product::getName, query.getKeyword())
                .or()
                .like(Product::getDescription, query.getKeyword())
            );
        }
        
        if (StringUtils.isNotBlank(query.getCategory())) {
            wrapper.eq(Product::getCategory, query.getCategory());
        }
        
        if (query.getMinPrice() != null) {
            wrapper.ge(Product::getPrice, query.getMinPrice());
        }
        
        if (query.getMaxPrice() != null) {
            wrapper.le(Product::getPrice, query.getMaxPrice());
        }
        
        wrapper.orderByDesc(Product::getCreateTime);
        
        // 分页查询
        Page<Product> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<Product> productPage = page(page, wrapper);
        
        // 转换为VO,补充图片等信息
        return productPage.convert(product -> {
            ProductVO vo = ProductVO.fromEntity(product);
            vo.setImages(productImageService.listByProductId(product.getId()));
            return vo;
        });
    }
    
    // 多租户查询
    @Override
    public List<Product> listByTenant(Long tenantId) {
        return list(new LambdaQueryWrapper<Product>()
            .eq(Product::getTenantId, tenantId)
            .eq(Product::getIsDeleted, 0));
    }
}

选择理由

  • 大幅减少样板代码,提升开发速度
  • 内置分页、逻辑删除等企业级特性
  • Lambda 表达式保证代码可重构性
  • 完善的中文文档和国内社区支持
  • 与 Spring Boot 深度集成

4.3 tk-mybatis 适用场景

推荐场景

  • 轻量级项目:小型应用、微服务中的简单模块
  • 国际化团队:需要英文文档,避免中文技术栈锁定
  • 已有 MyBatis 基础:希望渐进式增强,而非完全替换
  • 资源受限环境:IoT 设备、边缘计算节点等对 jar 包大小敏感的场景

案例:IoT 设备管理服务

// 轻量级实体
public class Device {
    private String deviceId; // 主键
    private String name;
    private String type;
    private String status; // ONLINE/OFFLINE
    private String location;
    private Date lastActiveTime;
    private String tenantId; // 多租户
    // getters/setters
}

// 专用Mapper
public interface DeviceMapper extends Mapper<Device>, MySqlMapper<Device> {
    // 扩展方法
    @Select("SELECT * FROM devices WHERE tenant_id = #{tenantId} AND status = 'ONLINE'")
    List<Device> selectOnlineByTenant(@Param("tenantId") String tenantId);
    
    @Update("UPDATE devices SET status = 'OFFLINE' WHERE last_active_time < #{beforeTime}")
    void offlineInactiveDevices(@Param("beforeTime") Date beforeTime);
}

// 服务层
@Service
@RequiredArgsConstructor
public class DeviceService {
    
    private final DeviceMapper deviceMapper;
    private final TenantContext tenantContext;
    
    // 基础CRUD使用通用方法
    public Device getDevice(String deviceId) {
        return deviceMapper.selectByPrimaryKey(deviceId);
    }
    
    public List<Device> searchDevices(DeviceQuery query) {
        Example example = new Example(Device.class);
        Example.Criteria criteria = example.createCriteria();
        
        // 多租户隔离
        criteria.andEqualTo("tenantId", tenantContext.getCurrentTenant());
        
        if (StringUtils.isNotEmpty(query.getName())) {
            criteria.andLike("name", "%" + query.getName() + "%");
        }
        
        if (StringUtils.isNotEmpty(query.getType())) {
            criteria.andEqualTo("type", query.getType());
        }
        
        if (StringUtils.isNotEmpty(query.getStatus())) {
            criteria.andEqualTo("status", query.getStatus());
        }
        
        example.orderBy(query.getOrderBy()).orderBy(query.getSort());
        
        // 分页
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        return deviceMapper.selectByExample(example);
    }
    
    @Scheduled(fixedRate = 300000) // 5分钟
    public void checkDeviceStatus() {
        // 自动下线长时间无活动的设备
        Date offlineThreshold = new Date(System.currentTimeMillis() - 10 * 60 * 1000); // 10分钟
        deviceMapper.offlineInactiveDevices(offlineThreshold);
        
        // 记录日志
        log.info("Device status check completed at {}", new Date());
    }
}

选择理由

  • 轻量级,jar 包小,启动快
  • 保留 MyBatis 原生体验,学习成本低
  • 国际化支持好,适合海外团队
  • 对资源受限环境友好
  • 侵入性小,可渐进式采用

五、迁移策略与混合架构

5.1 从 MyBatis 到增强框架的迁移路径

渐进式迁移策略

graph LR
    A[纯MyBatis] -->|新增模块使用| B[混合架构]
    B -->|核心模块重写| C[全量增强框架]

实施步骤

  1. 评估现有代码:识别可复用的 Mapper 和实体
  2. 创建适配层:保留原有接口,内部实现替换
    // 旧接口保持不变
    public interface UserMapper {
        User findById(Long id);
        List<User> findByCondition(Map<String, Object> params);
    }
    
    // 新实现
    @Repository
    public class UserMapperImpl implements UserMapper {
        @Autowired
        private UserPlusMapper userPlusMapper; // MyBatis Plus 实现
        
        @Override
        public User findById(Long id) {
            return userPlusMapper.selectById(id);
        }
        
        @Override
        public List<User> findByCondition(Map<String, Object> params) {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            if (params.containsKey("name")) {
                wrapper.like(User::getName, params.get("name"));
            }
            // ...其他条件
            return userPlusMapper.selectList(wrapper);
        }
    }
    
  3. 分模块迁移:先迁移非核心模块,验证稳定性
  4. 性能监控:迁移前后对比关键指标(响应时间、GC 频率)
  5. 逐步替换:当新模块稳定后,替换核心模块

5.2 混合架构:优势互补的实践

在复杂系统中,单一框架往往难以满足所有需求。混合架构可发挥各自优势:

推荐模式

  • 核心交易:原生 MyBatis(精确控制 SQL 和事务)
  • 管理后台:MyBatis Plus(快速开发,丰富特性)
  • 基础数据:tk-mybatis(轻量高效)

统一数据访问层

// 统一DAO接口
public interface UserDao {
    User getById(Long id);
    List<User> searchUsers(UserQuery query);
    Page<User> pageUsers(int pageNum, int pageSize);
    boolean save(User user);
    boolean update(User user);
    boolean delete(Long id);
}

// 多实现注册
@Configuration
public class DaoConfig {
    
    @Bean
    @Primary
    @ConditionalOnProperty(prefix = "dao", name = "type", havingValue = "mybatis-plus")
    public UserDao mybatisPlusUserDao(UserMapper userMapper) {
        return new MybatisPlusUserDao(userMapper);
    }
    
    @Bean
    @ConditionalOnProperty(prefix = "dao", name = "type", havingValue = "tk-mybatis")
    public UserDao tkMybatisUserDao(tkUserMapper userMapper) {
        return new TkMybatisUserDao(userMapper);
    }
    
    @Bean
    @ConditionalOnProperty(prefix = "dao", name = "type", havingValue = "native")
    public UserDao nativeMybatisUserDao(SqlSessionTemplate sqlSession) {
        return new NativeMybatisUserDao(sqlSession);
    }
}

配置切换

# application.yml
dao:
  type: mybatis-plus # 可切换为 tk-mybatis 或 native

优势

  • 模块化设计,技术栈解耦
  • 按需选择最优框架
  • 平滑迁移,降低风险
  • 团队可并行使用不同技术栈

六、技术选型决策树

graph TD
    A[项目需求分析] --> B{SQL复杂度}
    B -->|高<br>复杂报表/分析查询| C[选择原生MyBatis]
    B -->|低-中<br>标准CRUD| D{团队环境}
    
    D -->|国内团队<br>中文支持需求| E{功能需求}
    E -->|多租户/逻辑删除/AR模式| F[选择MyBatis Plus]
    E -->|仅需基础CRUD| G[选择tk-mybatis]
    
    D -->|国际团队<br>英文文档需求| H{资源限制}
    H -->|资源受限<br>边缘设备/IoT| I[选择tk-mybatis]
    H -->|资源充足| J[评估MyBatis Plus国际版]
    
    C --> K[保留原生MyBatis]
    F --> L[全面采用MyBatis Plus]
    G --> M[采用tk-mybatis]
    I --> N[采用tk-mybatis]
    J --> O[考虑MyBatis Plus或混合架构]

七、未来趋势与建议

7.1 技术演进方向

  1. MyBatis Plus

    • 强化国际化支持,完善英文文档
    • 优化性能,减少反射开销
    • 增强云原生支持(Kubernetes 配置、Service Mesh 集成)
    • 与 Jakarta EE 标准深度融合
  2. tk-mybatis

    • 增加更多数据库方言支持
    • 轻量级分页和逻辑删除内置
    • 与 Quarkus、Micronaut 等新框架更好集成
    • 增强类型安全(通过注解处理器)
  3. MyBatis Core

    • 响应式编程支持(Reactive MyBatis)
    • 原生 GraalVM 支持
    • 增强类型处理器系统
    • 简化配置(减少 XML 依赖)

7.2 选型建议

根据项目阶段

  • 初创项目/快速验证:MyBatis Plus(开发速度优先)
  • 成熟产品/重构:混合架构(核心模块原生 MyBatis,辅助模块增强框架)
  • 维护遗留系统:渐进式迁移,先 tk-mybatis 后 MyBatis Plus

根据团队构成

  • 国内团队:MyBatis Plus(完善中文生态)
  • 国际团队:tk-mybatis(国际化支持好)
  • 混合团队:核心模块原生 MyBatis,统一技术栈

根据系统特性

  • 高并发核心系统:原生 MyBatis + 手动优化
  • 管理后台/内部系统:MyBatis Plus
  • 微服务基础数据:tk-mybatis
  • IoT/边缘计算:tk-mybatis(轻量级)

八、结语:合适的技术才是最好的技术

在 MyBatis 生态中,没有"最好"的框架,只有"最合适"的框架。MyBatis 提供了基础与自由,MyBatis Plus 带来了效率与功能,tk-mybatis 保持了轻量与兼容。三者并非竞争关系,而是互补共生的技术选择。

核心建议

  1. 不要为了用框架而用框架:评估实际需求,避免过度设计
  2. 保持架构的可进化性:设计良好的抽象层,降低迁移成本
  3. 性能与效率的权衡:在 80% 的场景,开发效率比 10% 的性能提升更重要
  4. 团队熟悉度优先:熟悉 MyBatis 的团队使用 tk-mybatis 学习成本更低
  5. 渐进式演进:从简单开始,逐步引入增强特性

正如 MyBatis 创始人 Clinton Begin 所言:"The best framework is the one that gets out of your way when you need it to."(最好的框架是在你需要时不会妨碍你的框架。)

选择适合你项目的框架,让它成为你构建卓越软件的助力,而非束缚。

附录:快速参考指南

常用操作对比

插入数据

// MyBatis
userMapper.insert(user);

// MyBatis Plus
userMapper.insert(user); // 或 user.insert()

// tk-mybatis
userMapper.insert(user);

条件查询

// MyBatis
List<User> users = userMapper.selectByCondition(map);

// MyBatis Plus
List<User> users = userMapper.selectList(
    new LambdaQueryWrapper<User>()
        .eq(User::getAge, 25)
        .like(User::getName, "张")
);

// tk-mybatis
Example example = new Example(User.class);
example.createCriteria()
    .andEqualTo("age", 25)
    .andLike("name", "张%");
List<User> users = userMapper.selectByExample(example);

分页查询

// MyBatis + PageHelper
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();

// MyBatis Plus
Page<User> page = new Page<>(1, 10);
userMapper.selectPage(page, null);

// tk-mybatis + PageHelper
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();

学习资源

MyBatis

MyBatis Plus

tk-mybatis