你代码里直接使用 Example(而不是 UserExampleOrderExample 等表名专属类),说明你的项目使用的并不是 MyBatis 官方原生的 MyBatis Generator (MBG),而是国内非常主流的 TkMyBatis (通用 Mapper,作者 Abel533)

在 TkMyBatis 中,官方提供了一个统配所有实体类的通用泛型容器:tk.mybatis.mapper.entity.Example


揭秘极简风格:TkMyBatis 通用 Example 类实战指南

一、为什么你用的是统一的 Example

对比项官方原生 MyBatis GeneratorTkMyBatis 通用 Mapper
类的全类名自动生成在各实体包下的 com.xxx.model.UserExample统一引用 tk.mybatis.mapper.entity.Example
文件生成量每张表都需要生成一个数千行的 Java 文件零物理生成,由框架提供统一的通用类
实现原理编译期强类型绑定方法(如 andAgeEqualTo运行时反射 解析实体类属性或 @Column 注解

TkMyBatis 的核心设计思想是通过实体类型 Class<T> 初始化上下文,从而省去了庞大的代码生成负担。


二、基础语法与初始化机制

创建 Example 时,必须将实体类的 Class 传入构造函数:

import tk.mybatis.mapper.entity.Example;

// 1. 初始化,指定作用的实体类
Example example = new Example(User.class);

// 2. 创建条件容器
Example.Criteria criteria = example.createCriteria();

特别注意(新手最易踩坑点):
传给条件方法的字段名,默认是Java 实体类的属性名(驼峰命名,如 userIduserName,而不是数据库底层的下划线字段名(user_iduser_name),TkMyBatis 会自动完成映射转换。


三、高频实战场景代码

1. 基础 AND 条件查询

目标 SQL:

SELECT * FROM user 
WHERE status = 1 
  AND age >= 18 
  AND age <= 30;

Java 代码:

Example example = new Example(User.class);
example.createCriteria()
       .andEqualTo("status", 1)
       .andGreaterThanOrEqualTo("age", 18)
       .andLessThanOrEqualTo("age", 30);

List<User> users = userMapper.selectByExample(example);


2. 模糊查询 (LIKE) 与 集合过滤 (IN)

目标 SQL:

SELECT * FROM user 
WHERE user_name LIKE '%jack%' 
  AND role_id IN (1, 2, 3);

Java 代码:

Example example = new Example(User.class);
example.createCriteria()
       .andLike("userName", "%jack%")       // 属性名 userName,需手写 %
       .andIn("roleId", Arrays.asList(1, 2, 3));

List<User> users = userMapper.selectByExample(example);


3. 多分支 OR 查询

目标 SQL:

SELECT * FROM user 
WHERE (dept_id = 10 AND status = 1) 
   OR (dept_id = 20 AND role_id = 2);

Java 代码:

Example example = new Example(User.class);

// 第一个括号的分支 (deptId = 10 AND status = 1)
Example.Criteria criteria1 = example.createCriteria();
criteria1.andEqualTo("deptId", 10)
         .andEqualTo("status", 1);

// 新建第二个 Criteria 并用 OR 连接
Example.Criteria criteria2 = example.or();
criteria2.andEqualTo("deptId", 20)
         .andEqualTo("roleId", 2);

List<User> users = userMapper.selectByExample(example);


4. 排序 (ORDER BY) 与指定查询列 (SelectProperties)

TkMyBatis 在通用 Example 上扩展了非常便利的专属 API:

Example example = new Example(User.class);
example.createCriteria().andEqualTo("status", 1);

// 方式一:面向属性名的安全排序(推荐,防止 SQL 注入)
example.orderBy("createTime").desc().orderBy("id").asc();

// 方式二:直接书写 SQL 排序片段
// example.setOrderByClause("create_time DESC, id ASC");

// 仅查询指定列(相当于 SELECT id, user_name),避免 SELECT *
example.selectProperties("id", "userName");

List<User> users = userMapper.selectByExample(example);


5. 结合 PageHelper 分页查询

在基于 TkMyBatis 的工程中,分页通常与 PageHelper 原生搭配:

PageHelper.startPage(1, 10);

Example example = new Example(User.class);
example.createCriteria().andEqualTo("status", 1);
example.orderBy("createTime").desc();

List<User> list = userMapper.selectByExample(example);
PageInfo<User> pageInfo = new PageInfo<>(list);


四、动态条件拼接最佳实践

面对前端传入的动态搜索筛选(字段可能为空),常规写法是叠加 if 分支:

Example example = new Example(User.class);
Example.Criteria criteria = example.createCriteria();

if (dto.getStatus() != null) {
    criteria.andEqualTo("status", dto.getStatus());
}
if (StringUtils.hasText(dto.getUserName())) {
    criteria.andLike("userName", "%" + dto.getUserName() + "%");
}
if (dto.getRoleIds() != null && !dto.getRoleIds().isEmpty()) {
    criteria.andIn("roleId", dto.getRoleIds());
}

List<User> list = userMapper.selectByExample(example);


五、TkMyBatis 的 Example.builder 链式进阶语法

TkMyBatis 还提供了更现代的 Builder 风格,可以将上述逻辑进一步压缩:

Example example = Example.builder(User.class)
        .select("id", "userName", "status")
        .where(WeekendSqls.<User>custom()
                .andEqualTo(User::getStatus, 1)
                .andLike(User::getUserName, "%jack%"))
        .orderByDesc("createTime")
        .build();

List<User> users = userMapper.selectByExample(example);

提示:如果使用的是 WeekendSqls,支持通过 Java 8 方法引用(如 User::getUserName)来避免写错字符串属性名。


六、关键避坑点

  • 传参是属性名,不是列名:调用 andEqualTo("userId", 1),写 userId(Java 属性),不要user_id(数据库列名),否则会抛出找不到属性的反射异常。
  • **每次查询必须全新 new Example()**:不可跨请求复用 Example 单例,否则多线程并发时条件会混乱,且历史查询条件会被持续追加。
  • 空集合保护:使用 andIn("roleId", list) 时,务必先判断 list != null && !list.isEmpty()。如果传入空集合,拼接出的 SQL 为 WHERE role_id IN (),会导致数据库直接报语法错误。
Logo

一站式 AI 云服务平台

更多推荐