380 lines
17 KiB
Markdown
380 lines
17 KiB
Markdown
# 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 列。
|