Skip to content

About

mybatis帮助插件,日志、分页、简单增删改查等

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

58 Commits

Folders and files

Repository files navigation

mybatis-helper

Maven Central License Java

一个轻量级的 MyBatis 增强工具库,免写 XML、免写 Mapper 接口,一个 DbUtil 静态类搞定动态 SQL 查询、实体对象 CRUD、分页、事务管控,并提供编译期类型安全的 SQL DSL 构建器。

✨ 核心特性

特性 描述
免 XML 动态 SQL 通过 DbUtil 直接传入 SQL 字符串或 DSL 构建器执行查询,无需编写 Mapper XML
SelectBuilder DSL 完整的面向对象链式 SQL 构建器,支持子查询、多表 JOIN、EXISTS、OR 嵌套等复杂场景
强类型元模型 (APT) 编译期自动生成 Q 开头的表元模型类,媲美 QueryDSL/jOOQ 的类型安全体验
实体对象 CRUD 通过 JPA 注解(@Table、@Column、@Id、@Transient)自动映射,免写 SQL
主键生成策略 @KeySql 注解支持 IDENTITY 自增、UUID、SQL 序列、自定义生成器,插入后自动回填
分页查询 自动检测数据库方言,内置 MySQL、PostgreSQL、Oracle、SQL Server、达梦等分页支持
事务管控 Spring 环境自动参与 @Transactional;非 Spring 环境提供 executeInTransaction 手动事务
Spring Boot 自动配置 引入依赖即生效,零配置自动初始化
批量插入 基于 JDBC Batch 的高性能批量插入,支持事务参与和主键回填

📦 快速开始

环境要求

  • Java 8+
  • MyBatis 3.5+

Maven 依赖

Spring Boot 项目(推荐)

引入 Starter 即可零配置自动生效,自动注入 SqlSessionFactory 并初始化 DbUtil,所有 SQL 操作自动参与 Spring @Transactional 事务管理。

<dependency>
    <groupId>io.github.juoliii</groupId>
    <artifactId>mybatis-helper-springboot-starter</artifactId>
    <version>1.0.3</version>
</dependency>

非 Spring Boot 项目

仅引入核心库,需手动初始化:

<dependency>
    <groupId>io.github.juoliii</groupId>
    <artifactId>mybatis-helper-core</artifactId>
    <version>1.0.3</version>
</dependency>
// 手动初始化
DbUtil.init(sqlSessionFactory);

📖 功能详解

1. 基础 SQL 查询

无需编写 Mapper 接口或 XML 文件,直接通过 DbUtil 静态方法执行 SQL。所有查询均支持 MyBatis #{...} 占位符防止 SQL 注入。

// 查询列表 — 返回 Map
List<Map<String, Object>> list = DbUtil.selectList("SELECT * FROM sys_user WHERE status = #{status}", params);

// 查询列表 — 映射到实体类
List<SysUser> users = DbUtil.selectList("SELECT * FROM sys_user WHERE status = 1", SysUser.class);

// 查询单条
SysUser user = DbUtil.selectOne("SELECT * FROM sys_user WHERE id = #{id}", params, SysUser.class);

// 插入
int rows = DbUtil.insert("INSERT INTO sys_user(name) VALUES(#{name})", params);

// 更新
DbUtil.update("UPDATE sys_user SET status = 1 WHERE id = #{id}", params);

// 删除
DbUtil.delete("DELETE FROM sys_user WHERE status = -1");

2. QueryWrapper 条件构建器

QueryWrapper 提供链式 API 构建 WHERE 条件,所有参数自动使用预编译占位符防止 SQL 注入。

QueryWrapper wrapper = new QueryWrapper()
    .eq("status", 1)                              // status = ?
    .like("name", "test")                          // name LIKE '%test%'
    .in("type", Arrays.asList(1, 2, 3))            // type IN (?, ?, ?)
    .between("create_time", startDate, endDate)    // create_time BETWEEN ? AND ?
    .isNotNull("email")                            // email IS NOT NULL
    .orderByDesc("create_time");                   // ORDER BY create_time DESC

List<SysUser> users = DbUtil.selectByQuery("SELECT * FROM sys_user", wrapper, SysUser.class);

支持的条件方法

方法 说明 示例
eq(column, value) 等于 eq("status", 1)
ne(column, value) 不等于 ne("status", -1)
gt(column, value) 大于 gt("age", 18)
ge(column, value) 大于等于 ge("score", 60)
lt(column, value) 小于 lt("price", 100)
le(column, value) 小于等于 le("amount", 999)
like(column, value) 模糊匹配(自动添加 %) like("name", "test")
likeLeft(column, value) 左模糊 %value likeLeft("name", "test")
likeRight(column, value) 右模糊 value% likeRight("name", "test")
in(column, values) IN 查询 in("type", list)
notIn(column, values) NOT IN notIn("type", list)
between(column, start, end) 范围 between("age", 18, 30)
notBetween(column, start, end) 不在范围 notBetween("age", 18, 30)
isNull(column) IS NULL isNull("email")
isNotNull(column) IS NOT NULL isNotNull("email")
apply(sql) 原生 SQL 片段 apply("DATE(create_time) = CURDATE()")

所有条件方法均支持第三个 boolean condition 参数,用于动态决定是否拼接该条件:

QueryWrapper wrapper = new QueryWrapper()
    .eq("status", status)
    .like("name", keyword, keyword != null && !keyword.isEmpty())  // 仅当 keyword 非空时才拼接
    .ge("create_time", startDate, startDate != null);

OR / AND 嵌套

QueryWrapper wrapper = new QueryWrapper()
    .eq("status", 1)
    .or(sub -> sub.eq("type", 1).like("name", "vip"))     // ... AND (type = ? AND name LIKE ?) OR ...
    .and(sub -> sub.gt("age", 18).lt("age", 60));          // ... AND (age > ? AND age < ?)

子查询支持

QueryWrapper wrapper = new QueryWrapper()
    .exists(new SelectBuilder().select("1").from("sys_role r").where(w -> w.apply("r.user_id = u.id")))
    .inSql("dept_id", new SelectBuilder().select("id").from("sys_dept").where(w -> w.eq("status", 1)));

3. SelectBuilder 完整 SQL DSL

SelectBuilder 提供完整的链式 API 构建 SELECT 查询,支持多表 JOIN、子查询嵌套、EXISTS、GROUP BY / HAVING、ORDER BY 等所有标准 SQL 特性。

String keyword = "test";

SelectBuilder sb = new SelectBuilder()
    .select("u.id", "u.name", "d.name AS deptName")
    // SELECT 子查询
    .select(new SelectBuilder()
        .select("COUNT(*)").from("sys_role r")
        .where(sub -> sub.apply("r.user_id = u.id"))
        .as("roleCount"))
    // FROM 子查询
    .from(new SelectBuilder()
        .select("id", "name").from("sys_user")
        .where(w -> w.gt("id", 1))
        .as("u"))
    // LEFT JOIN 子查询
    .leftJoin(new SelectBuilder()
        .select("id", "status").from("sys_dept")
        .as("d"))
        .on(w -> w.apply("u.dept_id = d.id").eq("d.status", 1))
    // WHERE 条件
    .where(w -> w
        .eq("u.status", 1)
        .like("u.name", keyword, keyword != null)
        .in("u.type", Arrays.asList(1, 2, 3))
        .exists(new SelectBuilder()
            .select("1").from("sys_role r")
            .where(sub -> sub.apply("r.user_id = u.id").eq("r.role_name", "admin")))
        .or(sub -> sub.eq("u.type", 1).like("u.name", "vip")))
    .groupBy("u.dept_id")
    .having(w -> w.apply("COUNT(*) > 1").or(sub -> sub.apply("MAX(u.status) = 2")))
    .orderByDesc("u.create_time");

// 执行查询
List<Map<String, Object>> list = DbUtil.selectList(sb);
List<SysUser> users = DbUtil.selectList(sb, SysUser.class);

SelectBuilder.n() 可以作为 new SelectBuilder() 的简写:

SelectBuilder sb = SelectBuilder.n()
    .select("*").from("sys_user")
    .where(w -> w.eq("status", 1));

支持的 JOIN 类型

方法 说明
leftJoin(table).on(...) LEFT JOIN
rightJoin(table).on(...) RIGHT JOIN
innerJoin(table).on(...) INNER JOIN
crossJoin(table) CROSS JOIN

以上所有 JOIN 方法均支持三种参数类型:String 表名、BtTable 强类型表、SelectBuilder 子查询。


4. 编译期强类型 SQL DSL (APT 自动生成元模型)

本项目内建了 Annotation Processor (APT),在编译期自动拦截 @Table 注解的实体类,为其生成以 Q 开头的强类型元模型类(如 QSysUser),让你的 SQL 构建具备 100% 的编译期类型检查和 IDE 代码补全。

4.1 启用 APT

在使用方项目的 pom.xml 中添加编译器插件配置:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>8</source>
        <target>8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>io.github.juoliii</groupId>
                <artifactId>mybatis-helper-core</artifactId>
                <version>1.0.3</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

4.2 定义实体类

@Table(name = "sys_user")
public class SysUser {
    @Id
    private Long id;

    @Column(name = "user_name")
    private String userName;

    private Long deptId;       // 自动映射为 dept_id
    private Integer status;
    private String username;
    private Date createTime;   // 自动映射为 create_time
}

编译后自动生成 QSysUser.java:

public class QSysUser extends BtTable {
    public static final QSysUser SYS_USER = new QSysUser("sys_user");

    public static QSysUser as(String alias) {
        return new QSysUser(alias);
    }

    public QSysUser(String alias) {
        super("sys_user", alias);
    }

    public final BtColumn ID = createColumn("id");
    public final BtColumn USER_NAME = createColumn("user_name");
    public final BtColumn DEPT_ID = createColumn("dept_id");
    public final BtColumn STATUS = createColumn("status");
    public final BtColumn USERNAME = createColumn("username");
    public final BtColumn CREATE_TIME = createColumn("create_time");

    public final BtColumn[] ALL = new BtColumn[] { ID, USER_NAME, DEPT_ID, STATUS, USERNAME, CREATE_TIME };
}

4.3 使用强类型查询

QSysUser u = new QSysUser("u");
QSysDept d = new QSysDept("d");

SelectBuilder sb = new SelectBuilder()
    // u.ALL 可选取该表所有列
    .select(u.ALL)
    .select(d.NAME.as("deptName"))
    .from(u)
    // 两个 BtColumn 比较时,自动生成 u.dept_id = d.id(无占位符)
    .leftJoin(d).on(w -> w.eq(u.DEPT_ID, d.ID))
    // 与普通值比较时,自动生成 u.status = #{param}(有占位符)
    .where(w -> w.eq(u.STATUS, 1).like(u.USERNAME, "vip"))
    .groupBy(u.DEPT_ID)
    .orderByDesc(u.CREATE_TIME);

List<Map<String, Object>> list = DbUtil.selectList(sb);

核心优势:列名不再是字符串硬编码,重命名字段后编译器自动报错,IDE 全程代码补全。


5. 实体对象 CRUD(免写 SQL)

通过 JPA 注解自动解析表名、列名和主键,直接传入实体对象进行增删改操作。

5.1 实体类定义

注解 位置 说明
@Table(name = "...") 类 指定表名,缺省则将类名转为下划线命名
@Id 字段 标记主键字段
@Column(name = "...") 字段 指定列名,缺省则将字段名转为下划线命名
@Transient 字段 标记非数据库字段,不参与任何 SQL 操作
@KeySql(...) 字段 指定主键生成策略(配合 @Id 使用)
@Table(name = "sys_user")
public class SysUser {
    @Id
    @KeySql(type = IdGenerationType.IDENTITY)
    private Long id;

    @Column(name = "user_name")
    private String userName;

    private String email;       // 自动映射为 email 列

    @Transient
    private String tempField;   // 不参与数据库操作
}

5.2 插入

SysUser user = new SysUser();
user.setUserName("test");
user.setEmail("test@example.com");

// 插入所有字段(包括 null 值的字段)
DbUtil.insert(user);

// 仅插入非 null 字段(Selective)
DbUtil.insertSelective(user);

5.3 批量插入

基于 JDBC ExecutorType.BATCH 高性能批量插入,而非逐条循环。支持参与外部事务、支持主键回填。

List<SysUser> users = new ArrayList<>();
users.add(user1);
users.add(user2);
users.add(user3);

// 批量插入(自动处理主键生成策略)
int rows = DbUtil.insert(users);

5.4 更新

SysUser user = new SysUser();
user.setId(1L);
user.setEmail("new@example.com");

// 按主键更新所有字段(null 值的字段也会被 SET 为 null)
DbUtil.update(user);

// 按主键仅更新非 null 的字段(Selective,推荐)
DbUtil.updateSelective(user);

5.5 删除

SysUser user = new SysUser();
user.setId(1L);

// 按主键删除
DbUtil.delete(user);

5.6 配合 QueryWrapper 的实体操作

// 按条件更新(不依赖主键,WHERE 由 QueryWrapper 指定)
SysUser updateEntity = new SysUser();
updateEntity.setStatus(0);
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("dept_id", 5);
DbUtil.updateByQuery(updateEntity, wrapper);
// 生成:UPDATE sys_user SET status = 0 WHERE dept_id = 5

// 按条件删除
DbUtil.deleteByQuery(SysUser.class, new QueryWrapper().lt("create_time", "2024-01-01"));
// 生成:DELETE FROM sys_user WHERE create_time < '2024-01-01'

6. 主键生成策略

通过 @KeySql 注解指定主键生成方式,插入后自动将生成的主键值回填到实体对象。

策略 说明 适用场景
NONE 不自动生成,用户手动赋值(默认) 业务主键
IDENTITY 数据库自增,插入后回填 MySQL AUTO_INCREMENT、PostgreSQL SERIAL
UUID 应用层生成 32 位无分隔符 UUID 分布式系统、无序主键
SQL 执行指定 SQL 获取主键值 Oracle 序列、PostgreSQL 序列
CUSTOM 自定义生成器 雪花算法、自定义编号规则

IDENTITY — 数据库自增

@Id
@KeySql(type = IdGenerationType.IDENTITY)
private Long id;

// 使用
SysUser user = new SysUser();
user.setUserName("test");
DbUtil.insert(user);
System.out.println(user.getId()); // 输出数据库生成的自增 ID,如:1001

IDENTITY 策略下,id 列不会出现在 INSERT 语句的列列表中,由数据库自增生成,插入完成后自动回填到实体对象。

UUID — 应用层自动生成

@Id
@KeySql(type = IdGenerationType.UUID)
private String id;

// 使用
SysUser user = new SysUser();
user.setUserName("test");
DbUtil.insert(user);
System.out.println(user.getId()); // 输出如:a1b2c3d4e5f6...(32位)

SQL — 执行 SQL 获取主键

适用于 Oracle 序列等场景,插入前先执行指定 SQL 获取主键值:

// Oracle 序列
@Id
@KeySql(type = IdGenerationType.SQL, sql = "SELECT sys_user_seq.nextval FROM DUAL")
private Long id;

// PostgreSQL 序列
@Id
@KeySql(type = IdGenerationType.SQL, sql = "SELECT nextval('sys_user_seq')")
private Long id;

CUSTOM — 自定义生成器

实现 GenId 接口编写自定义生成逻辑:

// 1. 实现 GenId 接口
public class SnowflakeGenId implements GenId<Long> {
    @Override
    public Long genId() {
        return SnowflakeIdWorker.nextId(); // 你的雪花算法实现
    }
}

// 2. 在实体类中使用
@Id
@KeySql(type = IdGenerationType.CUSTOM, genId = SnowflakeGenId.class)
private Long id;

自定义生成器类必须提供无参构造函数。


7. 分页查询

内置基于数据库方言的分页查询支持,通过 selectPage 系列方法实现,返回 PageResult 对象。

自动方言检测:无需手动配置,初始化时通过 JDBC DatabaseMetaData 自动识别数据库类型并匹配对应的分页方言。分页 COUNT 查询由 CountSqlOptimizer(基于 JSqlParser)智能优化——自动移除 ORDER BY / LIMIT,简单查询直接替换 SELECT 为 COUNT(*),复杂查询(DISTINCT / GROUP BY / UNION)包装为子查询。

内置支持的数据库

数据库 分页语法
MySQL / MariaDB / SQLite / H2 LIMIT ... OFFSET ...
PostgreSQL / KingbaseES / openGauss LIMIT ... OFFSET ...
Oracle(12c+) OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
SQL Server(2012+) OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
达梦(DM) LIMIT ... OFFSET ...

基本用法

// 直接 SQL 分页,pageNumber 从 1 开始
PageResult<SysUser> page = DbUtil.selectPage(
    "SELECT * FROM sys_user WHERE status = 1",
    SysUser.class, 1, 10  // 第1页,每页10条
);

// 带参数的分页查询
Map<String, Object> params = new HashMap<>();
params.put("status", 1);
PageResult<Map<String, Object>> page = DbUtil.selectPage(
    "SELECT * FROM sys_user WHERE status = #{status}",
    params, 1, 10
);

配合 SelectBuilder 分页

SelectBuilder sb = SelectBuilder.n()
    .select("*")
    .from("sys_user")
    .where(w -> w.eq("status", 1))
    .orderByDesc("create_time");

PageResult<SysUser> page = DbUtil.selectPage(sb, SysUser.class, 2, 20);

配合 QueryWrapper 分页

QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("status", 1).like("name", "test");

PageResult<SysUser> page = DbUtil.selectPageByQuery(
    "SELECT * FROM sys_user", wrapper, SysUser.class, 1, 10
);

PageResult 字段说明

字段 类型 说明
list List<T> 当前页数据列表
total long 总记录数
pageNumber int 当前页码(从 1 开始)
pageSize int 每页条数
pages int 总页数
PageResult<SysUser> page = DbUtil.selectPage(sql, SysUser.class, 1, 10);

List<SysUser> users = page.getList();       // 当前页数据
long total = page.getTotal();                // 总记录数
int pages = page.getPages();                 // 总页数
boolean hasNext = page.hasNext();            // 是否有下一页
boolean hasPrev = page.hasPrevious();        // 是否有上一页

自定义方言扩展

如果使用的数据库不在内置支持列表中,可以通过以下方式扩展:

// 方式一:注册自定义方言(按数据库产品名匹配)
DialectRegistry.registerDialect("gbase", new DialectFactory() {
    @Override
    public String buildPaginationSql(String sql, int offset, int limit) {
        return sql + " LIMIT " + limit + " OFFSET " + offset;
    }

    @Override
    public String buildCountSql(String sql) {
        return "SELECT COUNT(*) FROM (" + sql + ") _t";
    }
});

// 方式二:直接指定方言(跳过自动检测)
DialectRegistry.setDialect(new MySqlDialect());

8. 事务支持

Spring 环境

在 Spring Boot 环境中,DbUtil 自动参与 Spring 的 @Transactional 事务管理。你只需像往常一样使用 Spring 事务注解即可。

非 Spring 环境

默认每次调用 CRUD 方法都会自动开启新的 Session 并自动提交。如果需要在同一个事务中执行多次操作,使用 DbUtil.executeInTransaction:

// 无返回值的事务操作
DbUtil.executeInTransaction(() -> {
    // 以下操作在同一个底层 SqlSession 事务中执行
    DbUtil.insert(user1);
    DbUtil.insert(user2);
    DbUtil.update(user3);
    // 如果发生异常,事务自动回滚;全部成功则自动 commit
});

// 带返回值的事务操作
boolean success = DbUtil.executeInTransaction(() -> {
    DbUtil.insert(user1);
    return true;
});

注意:如果在 Spring 环境中调用 executeInTransaction,该方法会安全降级,直接执行传入的逻辑,事务管理交由 Spring @Transactional 统一接管。executeInTransaction 也支持嵌套调用。


🏗️ 项目架构

mybatis-helper
├── com.bitian.db.mybatis_helper
│   ├── apt/                    # APT 编译期处理器
│   │   └── BtMetaProcessor        # 自动生成 Q 开头的强类型元模型类
│   ├── dialect/                # 数据库分页方言
│   │   ├── DialectFactory          # 方言接口
│   │   ├── DialectRegistry         # 方言注册中心 & 自动检测
│   │   ├── CountSqlOptimizer       # 智能 COUNT SQL 优化(基于 JSqlParser)
│   │   ├── MySqlDialect            # MySQL / MariaDB / SQLite / H2
│   │   ├── PostgreSqlDialect       # PostgreSQL / KingbaseES / openGauss
│   │   ├── OracleDialect           # Oracle 12c+
│   │   ├── SqlServerDialect        # SQL Server 2012+
│   │   └── DmDialect               # 达梦 DM
│   ├── mapper/                 # MyBatis Mapper
│   │   └── DbMapper               # 内部动态 SQL 执行 Mapper
│   ├── meta/                   # 元数据模型
│   │   ├── BtTable / BtColumn      # 强类型 DSL 核心元模型
│   │   ├── EntityResolver          # JPA 注解实体类元数据解析器
│   │   ├── EntityMetadata          # 实体类元数据封装
│   │   ├── ColumnInfo              # 列信息
│   │   ├── KeySql                  # 主键生成策略注解
│   │   ├── IdGenerationType        # 主键生成策略枚举
│   │   ├── GenId                   # 自定义主键生成器接口
│   │   ├── UUIDGenId               # 内置 UUID 生成器
│   │   └── PageResult              # 分页结果封装
│   ├── provider/               # MyBatis SQL Provider
│   │   └── DbProvider              # 动态 SQL 提供器
│   ├── spring/                 # Spring Boot 集成
│   │   └── DbUtilAutoConfiguration # 自动配置类
│   └── util/                   # 核心工具
│       ├── DbUtil                  # 核心入口(查询、CRUD、分页、事务)
│       ├── SelectBuilder           # 完整 SQL DSL 构建器
│       └── QueryWrapper            # WHERE 条件构建器

🔗 API 快速参考

DbUtil 查询方法

方法 说明
selectList(sql, params, clazz) SQL 查询,返回实体类列表
selectList(sql, params) SQL 查询,返回 Map 列表
selectList(builder, clazz) SelectBuilder 查询,返回实体类列表
selectList(builder) SelectBuilder 查询,返回 Map 列表
selectOne(sql, params, clazz) SQL 查询,返回单个实体
selectOne(sql, params) SQL 查询,返回单个 Map
selectByQuery(baseSql, wrapper, clazz) QueryWrapper 查询,返回实体类列表
selectOneByQuery(baseSql, wrapper, clazz) QueryWrapper 查询,返回单个实体

DbUtil 分页方法

方法 说明
selectPage(sql, params, clazz, pageNumber, pageSize) SQL 分页查询,返回实体类
selectPage(sql, params, pageNumber, pageSize) SQL 分页查询,返回 Map
selectPage(builder, clazz, pageNumber, pageSize) SelectBuilder 分页查询
selectPageByQuery(baseSql, wrapper, clazz, pageNumber, pageSize) QueryWrapper 分页查询

DbUtil 实体 CRUD 方法

方法 说明
insert(entity) 插入实体对象(所有字段)
insertSelective(entity) 插入实体对象(仅非 null 字段)
insert(list) 批量插入(JDBC Batch)
update(entity) 按主键更新(所有字段)
updateSelective(entity) 按主键更新(仅非 null 字段)
updateByQuery(entity, wrapper) 按 QueryWrapper 条件更新
delete(entity) 按主键删除
deleteByQuery(clazz, wrapper) 按 QueryWrapper 条件删除

DbUtil 事务方法

方法 说明
executeInTransaction(Runnable) 在事务中执行(无返回值)
executeInTransaction(Supplier<R>) 在事务中执行(有返回值)

📝 License

Apache License 2.0

About

mybatis帮助插件,日志、分页、简单增删改查等

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages