Files
tape-springboot-mybatis-plugin/README.md
iqudoo 45f5300e42 fix
2026-05-23 19:39:11 +08:00

380 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# tape-mybatis-generator-plugin
> MyBatis 代码生成插件(基于 MyBatis Generator **1.4.1** 开发Maven 插件建议使用 **1.4.0+**
## 功能特性
本插件为 MyBatis Generator 提供了以下增强功能:
1. **TapeMybatisGeneratorPlugin** — 扩展 Mapper`selectPrimaryKeyByExample``batchInsert`)与 Example分页、回收站筛选等
2. **TapeTableRepositoryGeneratorPlugin** — 为非视图表生成 Repository 接口与实现,提供 CRUD、软删除与回收站
3. **TapeViewRepositoryGeneratorPlugin** — 为视图表生成 View Repository 接口与实现,提供只读查询
**视图表识别**:通过 JDBC 元数据 `tableType == "VIEW"` 判断;非视图表与视图表由不同插件分别生成代码。
**运行时依赖**:生成的 Repository 使用 Spring `@Repository``@Resource` 及 SLF4J使用方项目需引入对应依赖。
## 插件说明
### TapeMybatisGeneratorPlugin
扩展 MyBatis Mapper 与 Example 类:
**Mapper 接口 / XML**
- `selectPrimaryKeyByExample(Example)` — 按条件查询主键列表(支持 Example 分页 `limit`
- `batchInsert(List<Model>)` — 批量插入,返回影响行数
**Mapper XML 动态 SQL**
-`selectByExample``selectByExampleWithBLOBs``selectAll` 等查询追加 `limit` 片段(`rows` / `offset`
### TapeTableRepositoryGeneratorPlugin
为非视图表生成 Repository类名后缀由 `domainRepositoryType` 控制,默认 `Repository`
- **接口**`{facadeRepositoryPackage}.I{TableName}{domainRepositoryType}`
- **实现**`{domainRepositoryPackage}.{TableName}{domainRepositoryType}Impl`
示例(默认后缀):`IYourTableNameRepository``YourTableNameRepositoryImpl`
**生成的方法**(均声明 `throws Throwable`
| 方法 | 说明 |
|------|------|
| `trashById(long id)``int` | 移入回收站(`is_hidden=1` |
| `trashAll(Example)``int` | 批量移入回收站;自动限定 `is_delete=0``is_hidden=0` |
| `deleteById(long id, boolean release)``int` | `release=false` 软删;`release=true` 物理删除 |
| `deleteAll(Example, boolean release)``int` | `release=false` 仅软删回收站中记录(`is_hidden=1``release=true` 直接 `deleteByExample` |
| `recoverById(long id)``int` | 从回收站恢复 |
| `recoverAll(Example)``int` | 批量恢复;自动限定 `is_delete=0``is_hidden=1` |
| `findAnyById(long id)``Model` | 未删除(`is_delete=0`),不区分是否在回收站 |
| `findValidById(long id)``Model` | 有效数据(`is_hidden=0``is_delete=0` |
| `findTrashById(long id)``Model` | 回收站数据(`is_hidden=1``is_delete=0` |
| `findOne(Example)``Model` | 单条(内部 `usePage(1,1)` |
| `getList(Example)``List<Model>` | 列表;按 `resultType` 过滤;含 BLOB 且分页时先查主键再查详情 |
| `count(Example)``long` | 计数(含 `resultType` 过滤) |
| `countWithPage(Example)``long` | 仅当 `offset``rows` 均已设置时调用 `count`,否则返回 `0` |
| `insert(Model)``Model` | 插入并返回带 GUID 的记录 |
| `batchInsert(List<Model>)``List<Model>` | 批量插入并返回记录列表 |
| `update(Model)``int` | 按主键更新(支持乐观锁) |
| `updateByExampleSelective(Model, Example)``int` | 按条件选择性更新 |
### TapeViewRepositoryGeneratorPlugin
为视图表生成 View Repository类名后缀由 `domainViewRepositoryType` 控制,默认 `Repository`
- **接口**`{facadeViewRepositoryPackage}.I{TableName}{domainViewRepositoryType}`
- **实现**`{domainViewRepositoryPackage}.{TableName}{domainViewRepositoryType}Impl`
示例(默认后缀):`IYourTableNameRepository``YourTableNameRepositoryImpl`
**生成的方法**(均声明 `throws Throwable`
| 方法 | 说明 |
|------|------|
| `findOne(Example)``Model` | 单条 |
| `getList(Example)``List<Model>` | 列表(支持 Example 分页;含 BLOB 时按 `withBLOBs` 选择查询方法) |
| `count(Example)``long` | 计数 |
| `countWithPage(Example)``long` | 行为同表 Repository |
### Example 类增强TapeMybatisGeneratorPlugin
**所有表/视图共有的字段与方法**
| 字段 / 方法 | 说明 |
|------------|------|
| `offset``rows` | 分页偏移与条数 |
| `startPageNum` | 起始页码(默认 `1`,表级可覆盖) |
| `ignorePageSize` | `usePage` 时若 `pageSize >=` 该值则取消分页(默认 `10000` |
| `maxPageSize` | `usePage` 时每页上限(默认 `100` |
| `limit(rows)` / `limit(offset, rows)` | 设置分页 |
| `limitOffset(offset, rows)` | 设置 offset`rows` 置为极大值(仅偏移) |
| `usePage(pageNum, pageSize)` | 按页码计算 offset/rows |
| `getPageNum()` / `getPageSize()` / `getOffset()` / `getRows()` | 读取分页状态;未设 `rows``getPageSize()` 返回 `ignorePageSize` |
| `getLimitString()` / `getWhereString()` | 调试用 limit/where 字符串 |
| `cloneExample()` | 克隆条件与分页状态 |
| `get/setMaxPageSize``get/setIgnorePageSize``get/setStartPageNum` | 分页参数读写 |
**仅非视图表额外支持**
| 字段 / 方法 | 说明 |
|------------|------|
| `resultType` | `null`(默认,等同有效数据)、`any``trash``valid` |
| `setResultType` / `resultAny` / `resultTrash` / `resultValid` | 设置筛选类型 |
| `isResultAny` / `isResultTrash` / `isResultValid` | `isResultValid` 在既非 any 也非 trash 时为 true`resultType==null` |
**仅含 BLOB 列的表/视图**
| 字段 / 方法 | 说明 |
|------------|------|
| `withBLOBs` | 默认 `true` |
| `setWithBLOBs` / `isWithBLOBs` | 是否查询 BLOB 列 |
`getList` / `count` 在表 Repository 中会根据 `resultType` 自动附加 `is_hidden``is_delete` 条件。
## 使用方法
### 1. 在 `pom.xml` 中配置插件
```xml
<build>
<finalName>application</finalName>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<configurationFile>src/main/resources/mybatis.generator.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<phase>deploy</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.iqudoo.framework</groupId>
<artifactId>tape-mybatis-generator-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<systemPath>${project.basedir}/src/lib/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar</systemPath>
<scope>system</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
```
### 2. 在 `mybatis.generator.xml` 中配置插件
```xml
<context id="Mysql" targetRuntime="MyBatis3">
<property name="targetProject" value="src/main/java"/>
<property name="mapperPackage" value="com.iqudoo.platform.application.database.mapper"/>
<property name="modelPackage" value="com.iqudoo.platform.application.database.model"/>
<property name="facadeRepositoryPackage" value="com.iqudoo.platform.application.facade.repository"/>
<property name="domainRepositoryPackage" value="com.iqudoo.platform.application.domain.repository"/>
<property name="domainRepositoryType" value="Repository"/>
<property name="facadeViewRepositoryPackage" value="com.iqudoo.platform.application.facade.repoview"/>
<property name="domainViewRepositoryPackage" value="com.iqudoo.platform.application.domain.repoview"/>
<property name="domainViewRepositoryType" value="Repository"/>
<property name="guidGeneratorClass" value="com.iqudoo.framework.tape.modules.utils.SnowflakeUtil"/>
<property name="guidGeneratorCode" value="SnowflakeUtil.nextId()"/>
<property name="changeLogContextClassPackage" value="com.iqudoo.platform.application.domain.changeLog"/>
<property name="changeLogContextClassName" value="ChangeLogContext"/>
<property name="changeLogEnable" value="false"/>
<property name="slowQueryLoggerTime" value="300"/>
<property name="slowQueryLoggerLevel" value="error"/>
<property name="optimisticLockEnable" value="true"/>
<property name="ignorePageSize" value="10000"/>
<property name="startPageNum" value="1"/>
<property name="maxPageSize" value="100"/>
<plugin type="com.iqudoo.framework.mybatis.TapeMybatisGeneratorPlugin"/>
<plugin type="com.iqudoo.framework.mybatis.TapeTableRepositoryGeneratorPlugin"/>
<plugin type="com.iqudoo.framework.mybatis.TapeViewRepositoryGeneratorPlugin"/>
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<!-- 其他配置... -->
</context>
```
### 3. 全局配置参数说明
| 参数名 | 说明 | 默认值 | 必需 |
|--------|------|--------|------|
| `targetProject` | 生成代码的目标项目路径 | `src/main/java` | 否 |
| `mapperPackage` | Mapper 接口包路径 | `com.iqudoo.platform.application.database.mapper` | 是 |
| `modelPackage` | Model 类包路径 | `com.iqudoo.platform.application.database.model` | 是 |
| `facadeRepositoryPackage` | 表 Repository 接口包路径 | `com.iqudoo.platform.application.facade.repository` | 否 |
| `domainRepositoryPackage` | 表 Repository 实现类包路径 | `com.iqudoo.platform.application.domain.repository` | 否 |
| `domainRepositoryType` | 表 Repository 类名后缀 | `Repository` | 否 |
| `facadeViewRepositoryPackage` | 视图 Repository 接口包路径 | `com.iqudoo.platform.application.facade.repoview` | 否 |
| `domainViewRepositoryPackage` | 视图 Repository 实现类包路径 | `com.iqudoo.platform.application.domain.repoview` | 否 |
| `domainViewRepositoryType` | 视图 Repository 类名后缀 | `Repository` | 否 |
| `guidGeneratorClass` | GUID 生成工具类(需 import | `com.iqudoo.framework.tape.modules.utils.SnowflakeUtil` | 否 |
| `guidGeneratorCode` | GUID 生成表达式(写入生成代码) | `SnowflakeUtil.nextId()` | 否 |
| `changeLogContextClassPackage` | 变更日志上下文包路径 | `com.iqudoo.platform.application.domain.changeLog` | 否 |
| `changeLogContextClassName` | 变更日志上下文类名 | `ChangeLogContext` | 否 |
| `changeLogEnable` | 变更日志开关 | `false` | 否 |
| `slowQueryLoggerTime` | 慢查询阈值(毫秒) | `300` | 否 |
| `slowQueryLoggerLevel` | 慢查询日志级别error / warn / debug / info | `error` | 否 |
| `optimisticLockEnable` | 乐观锁开关 | `true` | 否 |
| `ignorePageSize` | `usePage` 取消分页的 pageSize 阈值 | `10000` | 否 |
| `startPageNum` | 分页起始页码 | `1` | 否 |
| `maxPageSize` | 单页最大条数 | `100` | 否 |
### 4. TABLE 级配置
表级 `<property>` 优先于全局 `<property>`,未配置时回退到全局值。
| 参数名 | 说明 |
|--------|------|
| `changeLogEnable` | 变更日志开关 |
| `slowQueryLoggerTime` | 慢查询阈值(毫秒) |
| `slowQueryLoggerLevel` | 慢查询日志级别 |
| `optimisticLockEnable` | 乐观锁开关 |
| `ignorePageSize` | 取消分页阈值 |
| `startPageNum` | 起始页码 |
| `maxPageSize` | 单页最大条数 |
示例:
```xml
<table tableName="your_table_name"
domainObjectName="YourTableName"
enableInsert="true"
enableDeleteByPrimaryKey="true"
enableUpdateByPrimaryKey="true"
enableCountByExample="true"
enableUpdateByExample="true"
enableDeleteByExample="true"
enableSelectByExample="true"
selectByExampleQueryId="false">
<property name="useActualColumnNames" value="false"/>
<property name="changeLogEnable" value="false"/>
<property name="slowQueryLoggerTime" value="200"/>
<property name="slowQueryLoggerLevel" value="warn"/>
<property name="optimisticLockEnable" value="false"/>
<property name="ignorePageSize" value="10000"/>
<property name="startPageNum" value="1"/>
<property name="maxPageSize" value="100"/>
</table>
```
## 变更日志监听
`changeLogEnable=true`Repository 实现会调用 `ChangeLogContext.addLog`。使用方需提供如下静态方法:
```java
public class ChangeLogContext {
public static void addLog(String tableName, String eventType, Long dataGuid,
Map<String, Object[]> fieldChanges) {
// 自行实现
}
}
```
**eventType 取值**
| eventType | 触发操作 |
|-----------|----------|
| `insert` | `insert` |
| `batchInsert` | `batchInsert` |
| `update` | `update` |
| `updateByExampleSelective` | `updateByExampleSelective` |
| `deleteById` | `deleteById`(软删) |
| `deleteAll` | `deleteAll`(软删) |
| `trashById` | `trashById` |
| `trashAll` | `trashAll` |
| `recoverById` | `recoverById` |
| `recoverAll` | `recoverAll` |
`fieldChanges``Object[]``[旧值, 新值]`;插入/删除类事件通常为空 `Map`
## 数据库表结构要求
### 标准表结构模板
使用完整 Repository 功能(软删除、回收站、乐观锁)时,表需包含以下标准字段:
```mysql
DROP TABLE IF EXISTS `your_table_name`;
CREATE TABLE `your_table_name` (
`guid` bigint(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'GUID',
-- 业务字段
`is_hidden` int(0) NOT NULL DEFAULT 0 COMMENT '隐藏标志',
`is_delete` int(0) NOT NULL DEFAULT 0 COMMENT '删除标志',
`delete_token` varchar(32) NULL DEFAULT '' COMMENT '删除令牌',
`data_version` int(0) NOT NULL DEFAULT 0 COMMENT '数据版本',
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '更新时间',
PRIMARY KEY (`guid`) USING BTREE,
KEY `idx_common_query` (`is_hidden`, `is_delete`, `create_time` desc, `guid`) USING BTREE
) ENGINE = InnoDB COMMENT = '你的表格备注'
ROW_FORMAT = Dynamic;
```
### 索引优化指南
唯一键索引(含 `delete_token` 以支持回收站后重建):
```sql
UNIQUE KEY `idx_your_unique_key` (
`your_unique_key`,
`delete_token`
) USING BTREE
```
查询索引:
```sql
KEY `idx_your_query_case` (
`is_hidden`, `is_delete`,
`create_time` desc,
`guid`
) USING BTREE
```
**标准字段说明**
| 字段 | 说明 |
|------|------|
| `guid` | 主键,`bigint UNSIGNED` |
| `is_hidden` | 回收站标志(`1` = 在回收站) |
| `is_delete` | 软删除标志 |
| `delete_token` | 删除令牌(有效数据为 `VALID`,进回收站后变更) |
| `data_version` | 乐观锁版本号 |
| `create_time` / `update_time` | 插入时自动设置 |
### 视图表
视图表无需上述标准字段,仅需业务列;由 `TapeViewRepositoryGeneratorPlugin` 生成只读 Repository。
### 在 `mybatis.generator.xml` 中配置表
```xml
<table tableName="your_table_name"
domainObjectName="YourTableName"
enableInsert="true"
enableDeleteByPrimaryKey="true"
enableUpdateByPrimaryKey="true"
enableCountByExample="true"
enableUpdateByExample="true"
enableDeleteByExample="true"
enableSelectByExample="true"
selectByExampleQueryId="false">
<property name="useActualColumnNames" value="false"/>
</table>
```
## 非视图表注意事项
1. **主键**:必须有名为 `guid` 的主键,类型 `bigint UNSIGNED`
2. **视图表判定**JDBC `tableType``VIEW` 时不会生成表 Repository改由视图插件处理。
3. **分页**Example 支持 `usePage()` / `limit()``pageSize >= ignorePageSize` 时不加分页。
4. **乐观锁**`update` 使用 `data_version``optimisticLockEnable=false` 时不校验版本。
5. **软删除**`deleteById` / `deleteAll` 默认软删(`is_delete=1``release=true` 为物理删除。
6. **回收站**`trash*` 设置 `is_hidden=1``recover*` 恢复为 `is_hidden=0``delete_token=VALID`
7. **批量删除**`deleteAll(release=false)` 仅作用于已在回收站的记录。
8. **自动字段**`insert` / `batchInsert` 自动设置 `guid``guidGeneratorCode`)、`is_delete=0``is_hidden=0``delete_token=VALID``data_version=1`、时间戳等。
9. **BLOB**:含 BLOB 列时,`getList` 分页查询先 `selectPrimaryKeyByExample` 再按 GUID 拉取详情;可通过 `withBLOBs` 控制是否查询 BLOB 列。