Compare commits

6 Commits
main ... v1

Author SHA1 Message Date
iqudoo
0cfaea99c4 v1 2026-04-21 02:52:14 +08:00
iqudoo
eea9e5e6ee v1 2026-04-21 02:51:02 +08:00
iqudoo
114f69597b 修复:外部传入的 Example 不会被内部修改,完全无副作用 2026-04-21 00:56:35 +08:00
iqudoo
0281b89803 fix 2026-04-16 21:47:07 +08:00
iqudoo
b4488bf7f3 修复bug 2026-04-16 19:38:36 +08:00
iqudoo
959486fed0 V1 2026-04-16 19:20:44 +08:00
6 changed files with 1109 additions and 1827 deletions

379
README.md
View File

@@ -1,126 +1,92 @@
# tape-mybatis-generator-plugin
> MyBatis 代码生成插件(基于 MyBatis Generator **1.4.1** 开发Maven 插件建议使用 **1.4.0+**
> MyBatis 代码生成插件
## 功能特性
本插件为 MyBatis Generator 提供了以下增强功能:
1. **TapeMybatisGeneratorPlugin** 扩展 Mapper`selectPrimaryKeyByExample``batchInsert`)与 Example分页、回收站筛选等
2. **TapeTableRepositoryGeneratorPlugin** 为非视图表生成 Repository 接口实现,提供 CRUD软删除与回收站
3. **TapeViewRepositoryGeneratorPlugin** 为视图表生成 View Repository 接口实现,提供只读查询
**视图表识别**:通过 JDBC 元数据 `tableType == "VIEW"` 判断;非视图表与视图表由不同插件分别生成代码。
**运行时依赖**:生成的 Repository 使用 Spring `@Repository``@Resource` 及 SLF4J使用方项目需引入对应依赖。
1. **TapeMybatisGeneratorPlugin** - 扩展 MyBatis Mapper,添加 `selectPrimaryKeyByExample`, `batchInsert` 方法,拓展查询分页支持
2. **TapeRepositoryGeneratorPlugin** - 为非视图表自动生成 Repository 接口实现,提供完整的 CRUD软删除功能
3. **TapeRepoviewGeneratorPlugin** - 为视图表自动生成 RepoView 接口实现,提供查询功能
## 插件说明
### TapeMybatisGeneratorPlugin
扩展 MyBatis Mapper添加以下功能
- 在 Mapper 接口中添加 `selectPrimaryKeyByExample` 方法
- 在 Mapper XML 中生成对应的 SQL 查询语句
扩展 MyBatis Mapper 与 Example 类:
### TapeRepositoryGeneratorPlugin
为非视图表生成 Repository 层代码:
- **接口位置**: `{facadeRepositoryPackage}.I{TableName}Repository`
- **实现类位置**: `{domainRepositoryPackage}.{TableName}RepositoryImpl`
- **视图表过滤**: 根据 `viewKeyWords` 配置自动识别并跳过视图表
**Mapper 接口 / XML**
**生成的方法**
- `trashById(long id)` - 移动到回收站(单个)
- `trashAll({Example} example)` - 移动到回收站(批量)
- `deleteById(long id, boolean release)` - 删除(单个,支持物理删除)
- `deleteAll({Example} example, boolean release)` - 删除(批量,支持物理删除)
- `recoverById(long id)` - 从回收站恢复(单个)
- `recoverAll({Example} example)` - 从回收站恢复(批量)
- `findAnyById(long id)` - 查找(不区分有效/回收站)
- `findValidById(long id)` - 查找有效记录(单个)
- `findTrashById(long id)` - 查找回收站记录(单个)
- `findValidOne({Example} example)` - 查找有效记录(单个,支持条件)
- `findTrashOne({Example} example)` - 查找回收站记录(单个,支持条件)
- `getValidList({Example} example)` - 获取有效记录列表(支持分页)
- `getTrashList({Example} example)` - 获取回收站记录列表(支持分页)
- `countByValid({Example} example)` - 统计有效记录数
- `countByValidWithPage({Example} example)` - 统计分页有效记录数
- `countByTrash({Example} example)` - 统计回收站记录数
- `countByTrashWithPage({Example} example)` - 统计分页回收站记录数
- `insert({Model} record)` - 插入记录(自动生成 GUID、设置默认值
- `batchInsert(List<{Model}> records)` - 批量插入记录(自动生成 GUID、设置默认值
- `updateByExampleSelective({Model} record, {Example} example)` - 按条件更新记录
- `update({Model} record)` - 更新记录(支持乐观锁)
- `selectPrimaryKeyByExample(Example)` — 按条件查询主键列表(支持 Example 分页 `limit`
- `batchInsert(List<Model>)` — 批量插入,返回影响行数
### TapeRepoviewGeneratorPlugin
为视图表生成 RepoView 层代码:
- **接口位置**: `{facadeRepoviewPackage}.I{TableName}Repo`
- **实现类位置**: `{domainRepoviewPackage}.{TableName}RepoImpl`
- **视图表识别**: 仅处理包含 `viewKeyWords` 关键字的表
**Mapper XML 动态 SQL**
**生成的方法**
- `findOne({Example} example)` - 查找单条记录
- `getList({Example} example)` - 获取记录列表(支持分页)
- `count({Example} example)` - 统计记录数
-`selectByExample``selectByExampleWithBLOBs``selectAll` 等查询追加 `limit` 片段(`rows` / `offset`
### 其他功能
通过 `TapeMybatisGeneratorPlugin` 为所有 Example 类添加支持:
### TapeTableRepositoryGeneratorPlugin
**添加的字段**
- `offset` - 偏移量
- `rows` - 每页数量
- `startPageNum` - 最小页码(默认 1
- `ignorePageSize` - 忽略分页数量(默认 10000每页数量大于10000时忽略分页
- `maxPageSize` - 最大每页数量(默认 100
- `withBLOBs` - 是否返回BLOBs列的数据
为非视图表生成 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` 条件。
**添加的方法**
- `limit(int rows)` - 设置每页数量
- `limit(int offset, int rows)` - 设置偏移量和每页数量
- `usePage(int pageNum, int pageSize)` - 使用页码和每页数量(自动计算 offset
- `setWithBLOBs(boolean withBLOBs)` - 设置是否返回BLOBs列的数据
- `isWithBLOBs()` - 是否返回BLOBs列的数据
- `getPageNum()` - 获取当前页码
- `getPageSize()` - 获取当前每页数量
- `getOffset()` - 获取当前分页limit的offset
- `getRows()` - 获取当前分页limit的rows
## 使用方法
### 1. 在 `pom.xml` 中配置插件
```xml
<build>
<finalName>application</finalName>
<plugins>
<!-- your other plugins -->
<!-- add mybatis plugin -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
@@ -162,21 +128,21 @@
### 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="mapperPackage" value="com.iqudoo.platform.application.database.mapper"/>
<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="changeLogEnable" value="true"/>
<property name="slowQueryLoggerTime" value="300"/>
<property name="slowQueryLoggerLevel" value="error"/>
<property name="optimisticLockEnable" value="true"/>
@@ -184,123 +150,65 @@
<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"/>
<plugin type="com.iqudoo.framework.mybatis.TapeRepositoryGeneratorPlugin"/>
<plugin type="com.iqudoo.framework.mybatis.TapeRepoviewGeneratorPlugin"/>
<!-- 其他配置... -->
</context>
```
### 3. 全局配置参数说明
### 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()` | 否 |
| `modelPackage` | Model 类的包路径 | `com.iqudoo.platform.application.database.model` | 是 |
| `mapperPackage` | Mapper 接口的包路径 | `com.iqudoo.platform.application.database.mapper` | 是 |
| `facadeRepositoryPackage` | Repository 接口包路径 | `com.iqudoo.platform.application.facade.repository` | 否 |
| `domainRepositoryPackage` | Repository 实现类包路径 | `com.iqudoo.platform.application.domain.repository` | 否 |
| `facadeRepoviewPackage` | RepoView 接口的包路径 | `com.iqudoo.platform.application.facade.repoview` | 否 |
| `snowflakeUtilClass` | 雪花算法ID生成工具类 | `com.iqudoo.framework.tape.modules.utils.SnowflakeUtil` | 否 |
| `snowflakeUtilGenId` | 雪花算法ID生成方法 | `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>
```
| `changeLogContextClassName` | 变更日志上下文类 | `ChangeLogContext` | 否 |
| `changeLogEnable` | 变更日志监听开关 | `false` | 否 |
| `slowQueryLoggerTime` | 慢查询日志时间阈值 | `300` | 否 |
| `slowQueryLoggerLevel` | 慢查询日志类型errorwarndebuginfo | `error` | 否 |
| `ignorePageSize` | 忽略分页阈值 | `10000` | 否 |
| `startPageNum` | 分页开始页码 | `1` | 否 |
| `maxPageSize` | 最大每页数量 | `100` | 否 |
## 变更日志监听
`changeLogEnable=true` 时,Repository 实现调用 `ChangeLogContext.addLog`。使用方需提供如下静态方法:
ChangeLogContext应该提供以下实现的静态方法Repository实现调用
```java
public class ChangeLogContext {
public static void addLog(String tableName, String eventType, Long dataGuid,
Map<String, Object[]> fieldChanges) {
// 自行实现
/**
* 添加一条变更
*/
public static void addLog(String tableName, String eventType, Long dataGuid, Map<String, Object[]> fieldChanges) {
// your thing code
}
}
```
**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 功能(软删除、回收站、乐观锁)时,表需包含以下标准字段:
为了使用完整 Repository 功能(软删除、回收站等),表结构需要包含以下标准字段:
```mysql
DROP TABLE IF EXISTS `your_table_name`;
CREATE TABLE `your_table_name` (
`guid` bigint(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'GUID',
-- 业务字段
-- ----------------------------
-- add your table other table field
-- ----------------------------
`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 '删除令牌',
@@ -308,50 +216,34 @@ CREATE TABLE `your_table_name` (
`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;
-- UNIQUE KEY `idx_unique_key` (`you unique key`,`delete_token`) USING BTREE,
-- KEY `idx_common_query` (
-- 等值查询字段,
-- `is_hidden`,`is_delete`,
-- 其他参与排序字段,
-- `create_time` desc,
-- `guid`) USING BTREE
KEY `idx_common_query` (`is_hidden`,`is_delete`,`create_time` desc,`guid`) USING BTREE
) ENGINE = InnoDB COMMENT = '你的表格备注' ROW_FORMAT = Dynamic;
```
### 索引优化指南
**必需字段说明**
- `guid` - 主键,类型为 `bigint UNSIGNED`
- `is_hidden` - 隐藏标志,用于回收站功能
- `is_delete` - 删除标志,用于软删除功能
- `delete_token` - 删除令牌,用于标识删除状态
- `data_version` - 数据版本,用于乐观锁
- `create_time` - 创建时间,自动设置
- `update_time` - 更新时间,自动更新
唯一键索引(含 `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"
@@ -364,16 +256,31 @@ KEY `idx_your_query_case` (
selectByExampleQueryId="false">
<property name="useActualColumnNames" value="false"/>
</table>
<!-- 视图表配置(表名包含 VIEW_ 或 V_ 前缀,大小写不敏感) -->
<table tableName="v_your_view_name"
domainObjectName="ViewYourViewName"
enableInsert="false"
enableDeleteByPrimaryKey="false"
enableUpdateByPrimaryKey="false"
enableCountByExample="true"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="true"
selectByExampleQueryId="false">
<property name="useActualColumnNames" value="false"/>
<generatedKey column="guid" sqlStatement="JDBC" identity="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 列。
1. **主键要求**必须有一个主键字段,且字段名为 `guid`,类型 `bigint UNSIGNED`
2. **视图表识别**视图表通过表名中的关键字识别,默认关键字为 `view_` `v_` 不区分大小写
3. **分页功能**所有 Example 类都自动包含分页功能,可通过 `usePage()` 方法使用
4. **乐观锁**更新操作使用 `data_version` 字段实现乐观锁,更新时需要传入正确的版本
5. **软删除**删除操作默认软删除(设置 `is_delete` 标志),可通过 `release=true` 参数执行物理删除
6. **回收站**通过 `is_hidden` 字段实现回收站功能,`trash` 方法将记录移动到回收站,`recover` 方法恢复记录
7. **自动字段**:插入记录时,插件会自动设置 `guid`(使用雪花算法)、`is_delete``is_hidden``delete_token``data_version``create_time``update_time` 等字段
8. **BLOB 字段支持**:如果表包含 BLOB 字段,插件会自动使用 `selectByExampleWithBLOBs``updateByExampleWithBLOBs` 方法
9. **MyBatis Generator 版本**:本插件基于 MyBatis Generator 1.4.1 开发,建议使用 1.4.0 或更高版本

View File

@@ -11,7 +11,6 @@ import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.internal.util.StringUtility;
import java.util.List;
@@ -55,54 +54,9 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
}
}
private int getIgnorePageSize(IntrospectedTable introspectedTable) {
return getTableInt(introspectedTable, "ignorePageSize", ignorePageSize);
}
public int getMaxPageSize(IntrospectedTable introspectedTable) {
return getTableInt(introspectedTable, "maxPageSize", maxPageSize);
}
public int getStartPageNum(IntrospectedTable introspectedTable) {
return getTableInt(introspectedTable, "startPageNum", startPageNum);
}
// ====================== 统一 Table 属性获取(你全类都能用) ======================
private String getTableProperty(IntrospectedTable introspectedTable, String propName, String defaultValue) {
try {
TableConfiguration tc = introspectedTable.getTableConfiguration();
String v = tc.getProperty(propName);
if (StringUtility.stringHasValue(v)) {
return v.trim();
}
} catch (Exception ignored) {
}
return defaultValue;
}
private boolean getTableBool(IntrospectedTable introspectedTable, String propName, boolean defaultValue) {
String v = getTableProperty(introspectedTable, propName, String.valueOf(defaultValue));
try {
return Boolean.parseBoolean(v);
} catch (Exception e) {
return defaultValue;
}
}
private int getTableInt(IntrospectedTable introspectedTable, String propName, int defaultValue) {
String v = getTableProperty(introspectedTable, propName, String.valueOf(defaultValue));
try {
return Integer.parseInt(v);
} catch (Exception e) {
return defaultValue;
}
}
@SuppressWarnings("DuplicatedCode")
@Override
public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
topLevelClass.addImportedType(new FullyQualifiedJavaType("java.util.Objects"));
FullyQualifiedJavaType integerType = new FullyQualifiedJavaType("java.lang.Integer");
FullyQualifiedJavaType booleanType = new FullyQualifiedJavaType("java.lang.Boolean");
FullyQualifiedJavaType stringType = new FullyQualifiedJavaType("java.lang.String");
@@ -110,24 +64,12 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
String domainObjectName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();
String exampleClassName = domainObjectName + "Example";
if (!ElementTools.isViewTable(introspectedTable)) {
// any, trash, valid
Field resultType = ElementTools.generateField(
"resultType",
JavaVisibility.PROTECTED,
stringType,
"null"
);
topLevelClass.addField(resultType);
}
// 添加 startPageNum、maxPageSize、ignorePageSize 字段
Field maxPageSizeField = ElementTools.generateField(
"maxPageSize",
JavaVisibility.PROTECTED,
integerType,
getMaxPageSize(introspectedTable) + ""
this.maxPageSize + ""
);
topLevelClass.addField(maxPageSizeField);
@@ -135,7 +77,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
"ignorePageSize",
JavaVisibility.PROTECTED,
integerType,
getIgnorePageSize(introspectedTable) + ""
this.ignorePageSize + ""
);
topLevelClass.addField(ignorePageSizeField);
@@ -143,7 +85,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
"startPageNum",
JavaVisibility.PROTECTED,
integerType,
getStartPageNum(introspectedTable) + ""
startPageNum + ""
);
topLevelClass.addField(startPageNumField);
@@ -202,99 +144,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
Method mGetStartPageNum = ElementTools.generateGetterMethod(startPageNumField);
FormatTools.addMethodWithBestPosition(topLevelClass, mGetStartPageNum);
if (!ElementTools.isViewTable(introspectedTable)) {
// setResultType
Method setResultType = ElementTools.generateMethod(
"setResultType",
JavaVisibility.PUBLIC,
topLevelClass.getType(),
new Parameter(stringType, "type")
);
setResultType = ElementTools.generateMethodBody(
setResultType,
"this.resultType = type;",
"return this;"
);
FormatTools.addMethodWithBestPosition(topLevelClass, setResultType);
// resultValid
Method resultValid = ElementTools.generateMethod(
"resultValid",
JavaVisibility.PUBLIC,
topLevelClass.getType()
);
resultValid = ElementTools.generateMethodBody(
resultValid,
"this.resultType = \"valid\";",
"return this;"
);
FormatTools.addMethodWithBestPosition(topLevelClass, resultValid);
// isResultValid
Method isResultValid = ElementTools.generateMethod(
"isResultValid",
JavaVisibility.PUBLIC,
booleanType
);
isResultValid = ElementTools.generateMethodBody(
isResultValid,
"return !this.isResultAny() && !this.isResultTrash();"
);
FormatTools.addMethodWithBestPosition(topLevelClass, isResultValid);
// resultTrash
Method resultTrash = ElementTools.generateMethod(
"resultTrash",
JavaVisibility.PUBLIC,
topLevelClass.getType()
);
resultTrash = ElementTools.generateMethodBody(
resultTrash,
"this.resultType = \"trash\";",
"return this;"
);
FormatTools.addMethodWithBestPosition(topLevelClass, resultTrash);
// isResultTrash
Method isResultTrash = ElementTools.generateMethod(
"isResultTrash",
JavaVisibility.PUBLIC,
booleanType
);
isResultTrash = ElementTools.generateMethodBody(
isResultTrash,
"return Objects.equals(this.resultType, \"trash\");"
);
FormatTools.addMethodWithBestPosition(topLevelClass, isResultTrash);
// resultAny
Method resultAny = ElementTools.generateMethod(
"resultAny",
JavaVisibility.PUBLIC,
topLevelClass.getType()
);
resultAny = ElementTools.generateMethodBody(
resultAny,
"this.resultType = \"any\";",
"return this;"
);
FormatTools.addMethodWithBestPosition(topLevelClass, resultAny);
// isResultAny
Method isResultAny = ElementTools.generateMethod(
"isResultAny",
JavaVisibility.PUBLIC,
booleanType
);
isResultAny = ElementTools.generateMethodBody(
isResultAny,
"return Objects.equals(this.resultType, \"any\");"
);
FormatTools.addMethodWithBestPosition(topLevelClass, isResultAny);
}
// 提供几个快捷方法
Method setLimitByRows = ElementTools.generateMethod(
"limit",
@@ -310,7 +159,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, setLimitByRows);
// setLimitByOffsetRows
Method setLimitByOffsetRows = ElementTools.generateMethod(
"limit",
JavaVisibility.PUBLIC,
@@ -326,7 +174,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, setLimitByOffsetRows);
// limitOffset
Method setLimitByOffsetOnly = ElementTools.generateMethod(
"limitOffset",
JavaVisibility.PUBLIC,
@@ -342,7 +189,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, setLimitByOffsetOnly);
// usePage
Method usePage = ElementTools.generateMethod(
"usePage",
JavaVisibility.PUBLIC,
@@ -366,7 +212,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, usePage);
// getPageNum 计算获取当前页码
// 计算获取当前页码
Method getPageNum = ElementTools.generateMethod(
"getPageNum",
JavaVisibility.PUBLIC,
@@ -380,8 +226,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
"return this.offset / this.rows + this.startPageNum;"
);
FormatTools.addMethodWithBestPosition(topLevelClass, getPageNum);
// getPageSize 计算获取当前每页数量
// 计算获取当前每页数量
Method getPageSize = ElementTools.generateMethod(
"getPageSize",
JavaVisibility.PUBLIC,
@@ -396,7 +241,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, getPageSize);
// getRows
Method getRows = ElementTools.generateMethod(
"getRows",
JavaVisibility.PUBLIC,
@@ -408,7 +252,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, getRows);
// getOffset
Method getOffset = ElementTools.generateMethod(
"getOffset",
JavaVisibility.PUBLIC,
@@ -420,7 +263,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, getOffset);
// getLimitString
Method getLimitString = ElementTools.generateMethod(
"getLimitString",
JavaVisibility.PUBLIC,
@@ -439,7 +281,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, getLimitString);
// getWhereString
Method getWhereString = ElementTools.generateMethod(
"getWhereString",
JavaVisibility.PUBLIC,
@@ -475,7 +316,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
);
FormatTools.addMethodWithBestPosition(topLevelClass, getWhereString);
// cloneExample
Method cloneExample = ElementTools.generateMethod(
"cloneExample",
JavaVisibility.PUBLIC,
@@ -488,8 +329,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
"newExample.offset = this.offset;",
"newExample.maxPageSize = this.maxPageSize;",
"newExample.ignorePageSize = this.ignorePageSize;",
"newExample.startPageNum = this.startPageNum;",
!ElementTools.isViewTable(introspectedTable) ? "newExample.resultType = this.resultType;" : "",
"newExample.startPageNum = startPageNum;",
hasBLOBColumns ? "newExample.withBLOBs = withBLOBs;" : "",
"if (this.getOredCriteria() != null && !this.getOredCriteria().isEmpty()) {",
"for (" + exampleClassName + ".Criteria oldCriteria : this.getOredCriteria()) {",
@@ -509,9 +349,6 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
if (method.getName().equals("clear")) {
method.addBodyLine("rows = null;");
method.addBodyLine("offset = null;");
if (!ElementTools.isViewTable(introspectedTable)) {
method.addBodyLine("resultType = null;");
}
hasClear = true;
}
}
@@ -524,8 +361,7 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
clear = ElementTools.generateMethodBody(
clear,
"rows = null;",
"offset = null;",
!ElementTools.isViewTable(introspectedTable) ? "resultType = null;" : ""
"offset = null;"
);
FormatTools.addMethodWithBestPosition(topLevelClass, clear);
}
@@ -585,6 +421,9 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
batchInsert.setVisibility(JavaVisibility.PUBLIC);
batchInsert.setReturnType(FullyQualifiedJavaType.getIntInstance());
batchInsert.addParameter(recordsParam);
interfaceObj.addImportedType(new FullyQualifiedJavaType("java.util.List"));
interfaceObj.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
interfaceObj.addMethod(batchInsert);
}
@@ -630,12 +469,8 @@ public class TapeMybatisGeneratorPlugin extends PluginAdapter {
if (introspectedTable.getTargetRuntime() != IntrospectedTable.TargetRuntime.MYBATIS3) {
return super.clientGenerated(interfaceObj, introspectedTable);
}
interfaceObj.addImportedType(new FullyQualifiedJavaType("java.util.List"));
interfaceObj.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
addBatchInsertClientMethod(interfaceObj, introspectedTable);
if (introspectedTable.getPrimaryKeyColumns().isEmpty()) {
if (introspectedTable.getPrimaryKeyColumns().size() <= 0) {
return super.clientGenerated(interfaceObj, introspectedTable);
}
// 获取主键列类型

View File

@@ -0,0 +1,900 @@
package com.iqudoo.framework.mybatis;
import com.iqudoo.framework.mybatis.utils.ElementTools;
import com.iqudoo.framework.mybatis.utils.UtilTools;
import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.DefaultJavaFormatter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.internal.util.StringUtility;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"DuplicatedCode", "unused", "SpellCheckingInspection", "ExtractMethodRecommender"})
public class TapeRepositoryGeneratorPlugin extends PluginAdapter {
// 固定配置项
private String slowQueryLoggerTime = "300";
private String slowQueryLoggerLevel = "error";
private String facadeRepositoryPackage = "com.iqudoo.platform.application.facade.repository";
private String domainRepositoryPackage = "com.iqudoo.platform.application.domain.repository";
private String guidGeneratorClass = "com.iqudoo.framework.tape.modules.utils.SnowflakeUtil";
private String guidGeneratorCode = "SnowflakeUtil.nextId()";
private String changeLogContextClassPackage = "com.iqudoo.platform.application.domain.changeLog";
private String changeLogContextClassName = "ChangeLogContext";
private String changeLogEnable = "false";
private String optimisticLockEnable = "false";
private String modelPackage = "com.iqudoo.platform.application.database.model";
private String mapperPackage = "com.iqudoo.platform.application.database.mapper";
private String targetProject = "src/main/java";
// 1.4.1版本专用Java格式化器
private JavaFormatter javaFormatter;
@Override
public boolean validate(List<String> warnings) {
return true;
}
@Override
public void setContext(Context context) {
super.setContext(context);
this.javaFormatter = new DefaultJavaFormatter();
this.javaFormatter.setContext(context);
resolveConfiguration();
}
private void resolveConfiguration() {
slowQueryLoggerTime = stringConfig("slowQueryLoggerTime", slowQueryLoggerTime);
slowQueryLoggerLevel = stringConfig("slowQueryLoggerLevel", slowQueryLoggerLevel);
if (!UtilTools.inArray(new String[]{"error", "warn", "debug", "info"}, slowQueryLoggerLevel)) {
slowQueryLoggerLevel = "error";
}
guidGeneratorClass = stringConfig("guidGeneratorClass", guidGeneratorClass);
guidGeneratorCode = stringConfig("guidGeneratorCode", guidGeneratorCode);
facadeRepositoryPackage = stringConfig("facadeRepositoryPackage", facadeRepositoryPackage);
domainRepositoryPackage = stringConfig("domainRepositoryPackage", domainRepositoryPackage);
changeLogContextClassPackage = stringConfig("changeLogContextClassPackage", changeLogContextClassPackage);
changeLogContextClassName = stringConfig("changeLogContextClassName", changeLogContextClassName);
changeLogEnable = stringConfig("changeLogEnable", changeLogEnable);
optimisticLockEnable = stringConfig("optimisticLockEnable", optimisticLockEnable);
modelPackage = stringConfig("modelPackage", modelPackage);
mapperPackage = stringConfig("mapperPackage", mapperPackage);
targetProject = stringConfig("targetProject", targetProject);
}
private boolean isOptimisticLockEnable() {
try {
boolean b = Boolean.parseBoolean(optimisticLockEnable);
if (b) {
return true;
}
} catch (Throwable ignored) {
}
return false;
}
private boolean isChangeLogEnable() {
try {
boolean b = Boolean.parseBoolean(changeLogEnable);
if (b && changeLogContextClassName != null && !changeLogContextClassName.isEmpty()) {
return true;
}
} catch (Throwable ignored) {
}
return false;
}
private String stringConfig(String key, String defaultValue) {
String v = properties.getProperty(key);
if (StringUtility.stringHasValue(v)) {
return v;
}
if (context != null) {
v = context.getProperty(key);
if (StringUtility.stringHasValue(v)) {
return v;
}
}
return defaultValue;
}
/**
* 核心方法:视图表过滤 + 生成完整Repository代码
*/
@Override
public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
List<GeneratedJavaFile> generatedJavaFiles = new ArrayList<>();
// 视图表过滤逻辑
if (ElementTools.isViewTable(introspectedTable)) {
return generatedJavaFiles;
}
// 非视图表,正常生成
String domainObjectName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();
String exampleClassName = domainObjectName + "Example";
String mapperClassName = domainObjectName + "Mapper";
String repositoryInterfaceName = "I" + domainObjectName + "Repository";
String repositoryImplName = domainObjectName + "RepositoryImpl";
boolean hasBLOBColumns = ElementTools.hasBLOBColumns(introspectedTable);
// 生成Repository接口核心修改手动添加所有方法不再继承父接口
Interface repositoryInterface = generateRepositoryInterface(repositoryInterfaceName, domainObjectName, exampleClassName);
GeneratedJavaFile interfaceFile = new GeneratedJavaFile(
repositoryInterface,
targetProject,
"UTF-8",
javaFormatter
);
generatedJavaFiles.add(interfaceFile);
// 生成Repository实现类逻辑不变
TopLevelClass repositoryImpl = generateRepositoryImpl(
repositoryImplName,
repositoryInterfaceName,
domainObjectName,
exampleClassName,
mapperClassName,
introspectedTable,
hasBLOBColumns
);
GeneratedJavaFile implFile = new GeneratedJavaFile(
repositoryImpl,
targetProject,
"UTF-8",
javaFormatter
);
generatedJavaFiles.add(implFile);
// 手动写入磁盘
generateJavaFileToDisk(repositoryInterface, facadeRepositoryPackage);
generateJavaFileToDisk(repositoryImpl, domainRepositoryPackage);
return generatedJavaFiles;
}
/**
* 核心修改生成Repository接口手动添加所有方法无继承匹配指定格式
*/
private Interface generateRepositoryInterface(String interfaceName, String modelClassName, String exampleClassName) {
Interface repositoryInterface = new Interface(facadeRepositoryPackage + "." + interfaceName);
repositoryInterface.setVisibility(JavaVisibility.PUBLIC);
// 添加必要的导入
repositoryInterface.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + modelClassName));
repositoryInterface.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + exampleClassName));
repositoryInterface.addImportedType(new FullyQualifiedJavaType("java.util.List"));
repositoryInterface.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.session.RowBounds"));
// 1. deleteById
Method deleteByIdMethod = new Method("deleteById");
deleteByIdMethod.setVisibility(JavaVisibility.PUBLIC);
deleteByIdMethod.setReturnType(new FullyQualifiedJavaType("int"));
deleteByIdMethod.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id"));
deleteByIdMethod.addParameter(new Parameter(new FullyQualifiedJavaType("boolean"), "release"));
deleteByIdMethod.addException(new FullyQualifiedJavaType("Throwable"));
deleteByIdMethod.setAbstract(true);
repositoryInterface.addMethod(deleteByIdMethod);
// 2. deleteAll
Method deleteAllMethod = new Method("deleteAll");
deleteAllMethod.setVisibility(JavaVisibility.PUBLIC);
deleteAllMethod.setReturnType(new FullyQualifiedJavaType("int"));
deleteAllMethod.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
deleteAllMethod.addParameter(new Parameter(new FullyQualifiedJavaType("boolean"), "release"));
deleteAllMethod.addException(new FullyQualifiedJavaType("Throwable"));
deleteAllMethod.setAbstract(true);
repositoryInterface.addMethod(deleteAllMethod);
// 3. findById
Method findByIdMethod = new Method("findById");
findByIdMethod.setVisibility(JavaVisibility.PUBLIC);
findByIdMethod.setReturnType(new FullyQualifiedJavaType(modelClassName));
findByIdMethod.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id"));
findByIdMethod.addException(new FullyQualifiedJavaType("Throwable"));
findByIdMethod.setAbstract(true);
repositoryInterface.addMethod(findByIdMethod);
// 4. findOne
Method findOneMethod = new Method("findOne");
findOneMethod.setVisibility(JavaVisibility.PUBLIC);
findOneMethod.setReturnType(new FullyQualifiedJavaType(modelClassName));
findOneMethod.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
findOneMethod.addException(new FullyQualifiedJavaType("Throwable"));
findOneMethod.setAbstract(true);
repositoryInterface.addMethod(findOneMethod);
// 5. list
Method listMethod = new Method("list");
listMethod.setVisibility(JavaVisibility.PUBLIC);
listMethod.setReturnType(new FullyQualifiedJavaType("List<" + modelClassName + ">"));
listMethod.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
listMethod.addException(new FullyQualifiedJavaType("Throwable"));
listMethod.setAbstract(true);
repositoryInterface.addMethod(listMethod);
// 5. list
Method listWithRowBoundsMethod = new Method("list");
listWithRowBoundsMethod.setVisibility(JavaVisibility.PUBLIC);
listWithRowBoundsMethod.setReturnType(new FullyQualifiedJavaType("List<" + modelClassName + ">"));
listWithRowBoundsMethod.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
listWithRowBoundsMethod.addParameter(new Parameter(new FullyQualifiedJavaType("org.apache.ibatis.session.RowBounds"), "rowBounds"));
listWithRowBoundsMethod.addException(new FullyQualifiedJavaType("Throwable"));
listWithRowBoundsMethod.setAbstract(true);
repositoryInterface.addMethod(listWithRowBoundsMethod);
// 6. count
Method countMethod = new Method("count");
countMethod.setVisibility(JavaVisibility.PUBLIC);
countMethod.setReturnType(new FullyQualifiedJavaType("long"));
countMethod.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
countMethod.addException(new FullyQualifiedJavaType("Throwable"));
countMethod.setAbstract(true);
repositoryInterface.addMethod(countMethod);
// 7. insert
Method insertMethod = new Method("insert");
insertMethod.setVisibility(JavaVisibility.PUBLIC);
insertMethod.setReturnType(new FullyQualifiedJavaType(modelClassName));
insertMethod.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record"));
insertMethod.addException(new FullyQualifiedJavaType("Throwable"));
insertMethod.setAbstract(true);
repositoryInterface.addMethod(insertMethod);
// 8. update
Method updateMethod = new Method("update");
updateMethod.setVisibility(JavaVisibility.PUBLIC);
updateMethod.setReturnType(new FullyQualifiedJavaType("int"));
updateMethod.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record"));
updateMethod.addException(new FullyQualifiedJavaType("Throwable"));
updateMethod.setAbstract(true);
repositoryInterface.addMethod(updateMethod);
// 9. updateByExampleSelective
Method updateByExampleSelectiveMethod = new Method("updateByExampleSelective");
updateByExampleSelectiveMethod.setVisibility(JavaVisibility.PUBLIC);
updateByExampleSelectiveMethod.setReturnType(new FullyQualifiedJavaType("int"));
updateByExampleSelectiveMethod.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record"));
updateByExampleSelectiveMethod.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
updateByExampleSelectiveMethod.addException(new FullyQualifiedJavaType("Throwable"));
updateByExampleSelectiveMethod.setAbstract(true);
repositoryInterface.addMethod(updateByExampleSelectiveMethod);
return repositoryInterface;
}
/**
* 生成Repository实现类逻辑无修改
*/
private TopLevelClass generateRepositoryImpl(
String implClassName,
String interfaceName,
String modelClassName,
String exampleClassName,
String mapperClassName,
IntrospectedTable introspectedTable,
boolean hasBLOBColumns) {
TopLevelClass implClass = new TopLevelClass(domainRepositoryPackage + "." + implClassName);
implClass.setVisibility(JavaVisibility.PUBLIC);
implClass.addAnnotation("@SuppressWarnings(\"DuplicatedCode\")");
implClass.addAnnotation("@Repository");
addImportPackages(implClass, modelClassName, exampleClassName, mapperClassName, interfaceName);
FullyQualifiedJavaType superInterface = new FullyQualifiedJavaType(facadeRepositoryPackage + "." + interfaceName);
implClass.addSuperInterface(superInterface);
// slow query logger
Field loggerField = new Field("LOGGER", new FullyQualifiedJavaType("Logger"));
loggerField.setVisibility(JavaVisibility.PRIVATE);
loggerField.setStatic(true);
loggerField.setFinal(true);
loggerField.setInitializationString("LoggerFactory.getLogger(" + implClassName + ".class)");
implClass.addField(loggerField);
String mapperFieldName = lowerFirst(mapperClassName);
Field mapperField = new Field(mapperFieldName, new FullyQualifiedJavaType(mapperPackage + "." + mapperClassName));
mapperField.setVisibility(JavaVisibility.PRIVATE);
mapperField.addAnnotation("@Resource");
implClass.addField(mapperField);
// 原有方法生成逻辑(无修改)
generateFindByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName, hasBLOBColumns);
generateFindOneMethod(implClass, modelClassName, exampleClassName);
generateInsertMethod(implClass, modelClassName, mapperFieldName, introspectedTable);
generateUpdateMethod(implClass, modelClassName, exampleClassName, mapperFieldName, introspectedTable, hasBLOBColumns);
generateUpdateByExampleSelectiveMethod(implClass, modelClassName, exampleClassName, mapperFieldName, introspectedTable, hasBLOBColumns);
generateDeleteByIdMethod(implClass, modelClassName, mapperFieldName);
generateDeleteAllMethod(implClass, modelClassName, exampleClassName, mapperFieldName);
generateListMethod(implClass, modelClassName, exampleClassName, mapperFieldName, hasBLOBColumns);
generateListWithRowBoundsMethod(implClass, modelClassName, exampleClassName, mapperFieldName, hasBLOBColumns);
generateCountMethod(implClass, modelClassName, exampleClassName, mapperFieldName);
return implClass;
}
// ------------------------ 所有原有方法(无修改) ------------------------
private void generateJavaFileToDisk(Interface anInterface, String packageName) {
try {
String packagePath = packageName.replace('.', File.separatorChar);
File targetDir = new File(targetProject, packagePath);
if (!targetDir.exists()) {
boolean mkdirs = targetDir.mkdirs();
if (!mkdirs) {
throw new RuntimeException("Created dir: " + targetDir.getAbsolutePath());
}
}
String className = anInterface.getType().getShortName();
File javaFile = new File(targetDir, className + ".java");
String content = javaFormatter.getFormattedContent(anInterface);
java.nio.file.Files.write(javaFile.toPath(), content.getBytes(StandardCharsets.UTF_8));
System.out.println("Created: " + javaFile.getAbsolutePath());
} catch (Exception e) {
throw new RuntimeException("Create failure: " + anInterface.getType().getShortName(), e);
}
}
private void generateJavaFileToDisk(TopLevelClass clazz, String packageName) {
try {
String packagePath = packageName.replace('.', File.separatorChar);
File targetDir = new File(targetProject, packagePath);
if (!targetDir.exists()) {
boolean mkdirs = targetDir.mkdirs();
if (!mkdirs) {
throw new RuntimeException("Created dir: " + targetDir.getAbsolutePath());
}
}
String className = clazz.getType().getShortName();
File javaFile = new File(targetDir, className + ".java");
String content = javaFormatter.getFormattedContent(clazz);
java.nio.file.Files.write(javaFile.toPath(), content.getBytes(StandardCharsets.UTF_8));
System.out.println("Created: " + javaFile.getAbsolutePath());
} catch (Exception e) {
throw new RuntimeException("Create failure: " + clazz.getType().getShortName(), e);
}
}
private void addImportPackages(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperClassName, String interfaceName) {
implClass.addImportedType(new FullyQualifiedJavaType(guidGeneratorClass));
if (isChangeLogEnable()) {
implClass.addImportedType(new FullyQualifiedJavaType(changeLogContextClassPackage + "." + changeLogContextClassName));
}
implClass.addImportedType(new FullyQualifiedJavaType(mapperPackage + "." + mapperClassName));
implClass.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + modelClassName));
implClass.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + exampleClassName));
implClass.addImportedType(new FullyQualifiedJavaType(facadeRepositoryPackage + "." + interfaceName));
implClass.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.session.RowBounds"));
implClass.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Repository"));
implClass.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));
implClass.addImportedType(new FullyQualifiedJavaType("org.slf4j.Logger"));
implClass.addImportedType(new FullyQualifiedJavaType("org.slf4j.LoggerFactory"));
implClass.addImportedType(new FullyQualifiedJavaType("java.util.*"));
}
private void generateFindByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName, boolean hasBLOBColumns) {
Method method = new Method("findById");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType(modelClassName));
method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "guid"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine(modelClassName + " aDo = null;");
method.addBodyLine(exampleClassName + " example");
method.addBodyLine(" = new " + exampleClassName + "();");
method.addBodyLine("example.createCriteria()");
method.addBodyLine(" .andIsDeleteEqualTo(0)");
method.addBodyLine(" .andGuidEqualTo(guid);");
String selectByExampleMethod = hasBLOBColumns ? "selectByExampleWithBLOBs" : "selectByExample";
method.addBodyLine("List<" + modelClassName + "> recordList");
method.addBodyLine(" = " + mapperFieldName + "." + selectByExampleMethod + "(example);");
method.addBodyLine("if (recordList != null && !recordList.isEmpty()) {");
method.addBodyLine("aDo = recordList.get(0);");
method.addBodyLine("}");
method.addBodyLine("return aDo;");
implClass.addMethod(method);
}
private void generateFindOneMethod(TopLevelClass implClass, String modelClassName, String exampleClassName) {
Method method = new Method("findOne");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType(modelClassName));
method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine("// clone new example");
method.addBodyLine("example = example.cloneExample();");
method.addBodyLine("example.usePage(1, 1);");
method.addBodyLine("List<" + modelClassName + "> dataList = list(example);");
method.addBodyLine("if (dataList != null && !dataList.isEmpty()) {");
method.addBodyLine("return dataList.get(0);");
method.addBodyLine("}");
method.addBodyLine("return null;");
implClass.addMethod(method);
}
private void generateInsertMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, IntrospectedTable introspectedTable) {
Method method = new Method("insert");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType(modelClassName));
method.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine(modelClassName + " aDo = new " + modelClassName + "();");
method.addBodyLine("if (record.getGuid() != null) {");
method.addBodyLine("aDo.setGuid(record.getGuid());");
method.addBodyLine("} else {");
method.addBodyLine("Long guid = " + guidGeneratorCode + ";");
method.addBodyLine("aDo.setGuid(guid);");
method.addBodyLine("}");
for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
String fieldName = column.getJavaProperty();
String setterMethod = "set" + upperFirst(fieldName);
String getterMethod = "get" + upperFirst(fieldName);
if ("guid".equals(fieldName) || "isDelete".equals(fieldName)
|| "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName)
|| "createTime".equals(fieldName) || "updateTime".equals(fieldName)) {
continue;
}
method.addBodyLine("aDo." + setterMethod + "(record." + getterMethod + "());");
}
method.addBodyLine("aDo.setIsDelete(0);");
method.addBodyLine("aDo.setDeleteToken(\"VALID\");");
method.addBodyLine("aDo.setDataVersion(1);");
method.addBodyLine("aDo.setCreateTime(new Date());");
method.addBodyLine("aDo.setUpdateTime(new Date());");
method.addBodyLine("int count = " + mapperFieldName + ".insert(aDo);");
method.addBodyLine("if (count > 0) {");
method.addBodyLine("// optimistic locking with data version and guid");
method.addBodyLine("record.setGuid(aDo.getGuid());");
method.addBodyLine("record.setDataVersion(aDo.getDataVersion());");
method.addBodyLine("record.setCreateTime(aDo.getCreateTime());");
method.addBodyLine("record.setUpdateTime(aDo.getUpdateTime());");
if (isChangeLogEnable()) {
method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\",");
method.addBodyLine(" \"insert\", aDo.getGuid(), new HashMap<>());");
}
method.addBodyLine("return aDo;");
method.addBodyLine("}");
method.addBodyLine("throw new Throwable(\"Insert failed, " + modelClassName + ": \" + aDo);");
implClass.addMethod(method);
}
private void generateUpdateMethod(TopLevelClass implClass, String modelClassName, String exampleClassName,
String mapperFieldName, IntrospectedTable introspectedTable, boolean hasBLOBColumns) {
Method method = new Method("update");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("int"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine(modelClassName + " aDo");
method.addBodyLine(" = " + mapperFieldName + ".selectByPrimaryKey(record.getGuid());");
method.addBodyLine("if (aDo == null || aDo.getIsDelete() == 1) {");
method.addBodyLine("throw new Throwable(\"Record not found, " + modelClassName + " GUID:\" + record.getGuid());");
method.addBodyLine("}");
if (isChangeLogEnable()) {
method.addBodyLine("Map<String, Object[]> changeDiff = new HashMap<>();");
}
for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
String fieldName = column.getJavaProperty();
String setterMethod = "set" + upperFirst(fieldName);
String getterMethod = "get" + upperFirst(fieldName);
if ("guid".equals(fieldName) || "isDelete".equals(fieldName)
|| "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName)
|| "createTime".equals(fieldName) || "updateTime".equals(fieldName)) {
continue;
}
method.addBodyLine("if (record." + getterMethod + "() != null) {");
if (isChangeLogEnable()) {
method.addBodyLine("if (!Objects.equals(record." + getterMethod + "(), aDo." + getterMethod + "())) {");
method.addBodyLine("changeDiff.put(\"" + fieldName + "\", new Object[]{aDo." + getterMethod + "(), record." + getterMethod + "()});");
method.addBodyLine("}");
}
method.addBodyLine("aDo." + setterMethod + "(record." + getterMethod + "());");
method.addBodyLine("}");
}
method.addBodyLine(exampleClassName + " updateWhere = new " + exampleClassName + "();");
if (isOptimisticLockEnable()) {
method.addBodyLine("Integer lockDataVersion = record.getDataVersion();");
method.addBodyLine("if (lockDataVersion == null) {");
method.addBodyLine("lockDataVersion = aDo.getDataVersion();");
method.addBodyLine("}");
method.addBodyLine("updateWhere.createCriteria()");
method.addBodyLine(" .andGuidEqualTo(aDo.getGuid())");
method.addBodyLine(" .andDataVersionEqualTo(lockDataVersion);");
} else {
// 不使用乐观锁更新数据
method.addBodyLine("updateWhere.createCriteria()");
method.addBodyLine(" .andGuidEqualTo(aDo.getGuid());");
}
method.addBodyLine("aDo.setDataVersion(aDo.getDataVersion() + 1);");
method.addBodyLine("aDo.setUpdateTime(new Date());");
method.addBodyLine("// update data version");
method.addBodyLine("record.setDataVersion(aDo.getDataVersion());");
method.addBodyLine("record.setUpdateTime(aDo.getUpdateTime());");
String updateMethod = hasBLOBColumns ? "updateByExampleWithBLOBs" : "updateByExample";
if (isChangeLogEnable()) {
method.addBodyLine("int update = " + mapperFieldName + "." + updateMethod + "(aDo, updateWhere);");
method.addBodyLine("if (update > 0 && !changeDiff.isEmpty()) {");
method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\",");
method.addBodyLine(" \"update\", aDo.getGuid(), changeDiff);");
method.addBodyLine("}");
method.addBodyLine("return update;");
} else {
method.addBodyLine("return " + mapperFieldName + "." + updateMethod + "(aDo, updateWhere);");
}
implClass.addMethod(method);
}
private void generateUpdateByExampleSelectiveMethod(TopLevelClass implClass, String modelClassName, String exampleClassName,
String mapperFieldName, IntrospectedTable introspectedTable, boolean hasBLOBColumns) {
Method method = new Method("updateByExampleSelective");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("int"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
method.addException(new FullyQualifiedJavaType("Throwable"));
// 方法体
method.addBodyLine("// clone new example");
method.addBodyLine("example = example.cloneExample();");
method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {");
method.addBodyLine("criteria.andIsDeleteEqualTo(0);");
method.addBodyLine("}");
method.addBodyLine("List<Long> guidList = " + mapperFieldName);
method.addBodyLine(" .selectPrimaryKeyByExample(example);");
method.addBodyLine("if (guidList.isEmpty()) {");
method.addBodyLine("return 0;");
method.addBodyLine("}");
method.addBodyLine("example = new " + exampleClassName + "();");
method.addBodyLine("example.createCriteria()");
method.addBodyLine(" .andIsDeleteEqualTo(0)");
method.addBodyLine(" .andGuidIn(guidList);");
if (isChangeLogEnable()) {
String selectByExampleMethod = hasBLOBColumns ? "selectByExampleWithBLOBs" : "selectByExample";
method.addBodyLine("List<" + modelClassName + "> recordList");
method.addBodyLine(" = " + mapperFieldName + "." + selectByExampleMethod + "(example);");
method.addBodyLine("Map<Long, Map<String, Object[]>> diffGroup = new HashMap<>();");
method.addBodyLine("for (" + modelClassName + " aDo : recordList) {");
method.addBodyLine("Map<String, Object[]> changeDiff = new HashMap<>();");
for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
String fieldName = column.getJavaProperty();
String setterMethod = "set" + upperFirst(fieldName);
String getterMethod = "get" + upperFirst(fieldName);
if ("guid".equals(fieldName) || "isDelete".equals(fieldName)
|| "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName)
|| "createTime".equals(fieldName) || "updateTime".equals(fieldName)) {
continue;
}
method.addBodyLine("if (record." + getterMethod + "() != null && !Objects.equals(record." + getterMethod + "(), aDo." + getterMethod + "())) {");
method.addBodyLine("changeDiff.put(\"" + fieldName + "\", new Object[]{aDo." + getterMethod + "(), record." + getterMethod + "()});");
method.addBodyLine("}");
}
method.addBodyLine("diffGroup.put(aDo.getGuid(), changeDiff);");
method.addBodyLine("}");
}
method.addBodyLine("// reset data version to 100, with optimistic locking");
method.addBodyLine("record.setDataVersion(100);");
method.addBodyLine("// It is not supported to directly modify the following columns");
method.addBodyLine("record.setUpdateTime(new Date());");
method.addBodyLine("record.setIsDelete(null);");
method.addBodyLine("record.setDeleteToken(null);");
method.addBodyLine("record.setCreateTime(null);");
if (isChangeLogEnable()) {
method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(record, example);");
method.addBodyLine("if (update > 0) {");
method.addBodyLine("for (Map.Entry<Long, Map<String, Object[]>> diffEntry : diffGroup.entrySet()) {");
method.addBodyLine("if (diffEntry.getValue() != null && !diffEntry.getValue().isEmpty()) {");
method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\",");
method.addBodyLine(" \"updateByExampleSelective\", diffEntry.getKey(), diffEntry.getValue());");
method.addBodyLine("}");
method.addBodyLine("}");
method.addBodyLine("}");
method.addBodyLine("return update;");
} else {
method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(record, example);");
}
implClass.addMethod(method);
}
private void generateDeleteByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName) {
Method method = new Method("deleteById");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("int"));
method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id"));
method.addParameter(new Parameter(new FullyQualifiedJavaType("boolean"), "release"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine(modelClassName + " aDo = findById(id);");
method.addBodyLine("if (aDo == null) {");
method.addBodyLine("return 0;");
method.addBodyLine("}");
method.addBodyLine("if (release) {");
method.addBodyLine("return " + mapperFieldName + ".deleteByPrimaryKey(aDo.getGuid());");
method.addBodyLine("}");
method.addBodyLine("aDo.setIsDelete(1);");
method.addBodyLine("aDo.setUpdateTime(new Date());");
method.addBodyLine("int update = " + mapperFieldName + ".updateByPrimaryKey(aDo);");
if (isChangeLogEnable()) {
method.addBodyLine("if (update > 0) {");
method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\",");
method.addBodyLine(" \"deleteById\", aDo.getGuid(), new HashMap<>());");
method.addBodyLine("}");
}
method.addBodyLine("return update;");
implClass.addMethod(method);
}
private void generateDeleteAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) {
Method method = new Method("deleteAll");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("int"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
method.addParameter(new Parameter(new FullyQualifiedJavaType("boolean"), "release"));
method.addException(new FullyQualifiedJavaType("Throwable"));
// 方法体
method.addBodyLine("if (release) {");
method.addBodyLine("return " + mapperFieldName + ".deleteByExample(example);");
method.addBodyLine("}");
method.addBodyLine("// clone new example");
method.addBodyLine("example = example.cloneExample();");
method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {");
method.addBodyLine("criteria.andIsDeleteEqualTo(0);");
method.addBodyLine("}");
method.addBodyLine("List<Long> guidList = " + mapperFieldName + ".selectPrimaryKeyByExample(example);");
method.addBodyLine("if (guidList.isEmpty()) {");
method.addBodyLine("return 0;");
method.addBodyLine("}");
method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();");
method.addBodyLine(lowerFirst(modelClassName) + ".setIsDelete(1);");
method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());");
if (isChangeLogEnable()) {
method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);");
method.addBodyLine("if (update > 0) {");
method.addBodyLine("for (Long guid : guidList) {");
method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\",");
method.addBodyLine(" \"deleteAll\", guid, new HashMap<>());");
method.addBodyLine("}");
method.addBodyLine("}");
method.addBodyLine("return update;");
} else {
method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);");
}
implClass.addMethod(method);
}
private void generateListMethod(TopLevelClass implClass, String modelClassName, String exampleClassName,
String mapperFieldName, boolean hasBLOBColumns) {
Method method = new Method("list");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("List<" + modelClassName + ">"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine("// clone new example");
method.addBodyLine("example = example.cloneExample();");
method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {");
method.addBodyLine("criteria.andIsDeleteEqualTo(0);");
method.addBodyLine("}");
method.addBodyLine("List<" + modelClassName + "> result = null;");
method.addBodyLine("long startTime = new Date().getTime();");
if (hasBLOBColumns) {
method.addBodyLine("if (example.getRows() != null && example.getOffset() != null) {");
method.addBodyLine("List<Long> primaryKeyList = " + mapperFieldName + ".selectPrimaryKeyByExample(example);");
method.addBodyLine("if (primaryKeyList == null || primaryKeyList.isEmpty()) {");
method.addBodyLine("return new ArrayList<>();");
method.addBodyLine("}");
method.addBodyLine("long findPrimaryKeyTime = new Date().getTime() - startTime;");
method.addBodyLine("if (findPrimaryKeyTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
method.addBodyLine("}");
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("if (example.getLimitString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> limit: \" + example.getLimitString();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " valid list primary key use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + findPrimaryKeyTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
method.addBodyLine("}");
method.addBodyLine("// reset start time");
method.addBodyLine("startTime = new Date().getTime();");
method.addBodyLine("String oldOrderByClause = example.getOrderByClause();");
method.addBodyLine("Boolean withBLOBsFlag = example.isWithBLOBs();");
method.addBodyLine("example = new " + exampleClassName + "();");
method.addBodyLine("example.createCriteria().andGuidIn(primaryKeyList);");
method.addBodyLine("example.setOrderByClause(oldOrderByClause);");
method.addBodyLine("example.setWithBLOBs(withBLOBsFlag);");
method.addBodyLine("}");
method.addBodyLine("if (example.isWithBLOBs()) {");
method.addBodyLine("result = " + mapperFieldName + ".selectByExampleWithBLOBs(example);");
method.addBodyLine("} else {");
method.addBodyLine("result = " + mapperFieldName + ".selectByExample(example);");
method.addBodyLine("}");
} else {
method.addBodyLine("result = " + mapperFieldName + ".selectByExample(example);");
}
method.addBodyLine("long useTime = new Date().getTime() - startTime;");
method.addBodyLine("if (useTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
method.addBodyLine("}");
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("if (example.getLimitString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> limit: \" + example.getLimitString();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " valid list use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + useTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
method.addBodyLine("}");
method.addBodyLine("return result;");
implClass.addMethod(method);
}
private void generateListWithRowBoundsMethod(TopLevelClass implClass, String modelClassName, String exampleClassName,
String mapperFieldName, boolean hasBLOBColumns) {
Method method = new Method("list");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("List<" + modelClassName + ">"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
method.addParameter(new Parameter(new FullyQualifiedJavaType("org.apache.ibatis.session.RowBounds"), "rowBounds"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine("// 兼容原来的写法rowBounds");
method.addBodyLine("if (rowBounds != null) {");
method.addBodyLine("example.limit(rowBounds.getOffset(), rowBounds.getLimit());");
method.addBodyLine("}");
method.addBodyLine("// clone new example");
method.addBodyLine("example = example.cloneExample();");
method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {");
method.addBodyLine("criteria.andIsDeleteEqualTo(0);");
method.addBodyLine("}");
method.addBodyLine("List<" + modelClassName + "> result = null;");
method.addBodyLine("long startTime = new Date().getTime();");
if (hasBLOBColumns) {
method.addBodyLine("if (example.getRows() != null && example.getOffset() != null) {");
method.addBodyLine("List<Long> primaryKeyList = " + mapperFieldName + ".selectPrimaryKeyByExample(example);");
method.addBodyLine("if (primaryKeyList == null || primaryKeyList.isEmpty()) {");
method.addBodyLine("return new ArrayList<>();");
method.addBodyLine("}");
method.addBodyLine("long findPrimaryKeyTime = new Date().getTime() - startTime;");
method.addBodyLine("if (findPrimaryKeyTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
method.addBodyLine("}");
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("if (example.getLimitString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> limit: \" + example.getLimitString();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " valid list primary key use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + findPrimaryKeyTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
method.addBodyLine("}");
method.addBodyLine("// reset start time");
method.addBodyLine("startTime = new Date().getTime();");
method.addBodyLine("String oldOrderByClause = example.getOrderByClause();");
method.addBodyLine("Boolean withBLOBsFlag = example.isWithBLOBs();");
method.addBodyLine("example = new " + exampleClassName + "();");
method.addBodyLine("example.createCriteria().andGuidIn(primaryKeyList);");
method.addBodyLine("example.setOrderByClause(oldOrderByClause);");
method.addBodyLine("example.setWithBLOBs(withBLOBsFlag);");
method.addBodyLine("}");
method.addBodyLine("if (example.isWithBLOBs()) {");
method.addBodyLine("result = " + mapperFieldName + ".selectByExampleWithBLOBs(example);");
method.addBodyLine("} else {");
method.addBodyLine("result = " + mapperFieldName + ".selectByExample(example);");
method.addBodyLine("}");
} else {
method.addBodyLine("result = " + mapperFieldName + ".selectByExample(example);");
}
method.addBodyLine("long useTime = new Date().getTime() - startTime;");
method.addBodyLine("if (useTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
method.addBodyLine("}");
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("if (example.getLimitString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> limit: \" + example.getLimitString();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " valid list use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + useTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
method.addBodyLine("}");
method.addBodyLine("return result;");
implClass.addMethod(method);
}
private void generateCountMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) {
Method method = new Method("count");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
method.setReturnType(new FullyQualifiedJavaType("long"));
method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example"));
method.addException(new FullyQualifiedJavaType("Throwable"));
method.addBodyLine("// clone new example");
method.addBodyLine("example = example.cloneExample();");
method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {");
method.addBodyLine("criteria.andIsDeleteEqualTo(0);");
method.addBodyLine("}");
method.addBodyLine("long startTime = new Date().getTime();");
method.addBodyLine("long count = " + mapperFieldName + ".countByExample(example);");
method.addBodyLine("long useTime = new Date().getTime() - startTime;");
method.addBodyLine("if (useTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
method.addBodyLine("}");
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " valid count use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + useTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
method.addBodyLine("}");
method.addBodyLine("return count;");
implClass.addMethod(method);
}
private String upperFirst(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
private String lowerFirst(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toLowerCase() + str.substring(1);
}
}

View File

@@ -9,7 +9,6 @@ import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.DefaultJavaFormatter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.internal.util.StringUtility;
import java.io.File;
@@ -18,7 +17,7 @@ import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"DuplicatedCode", "SpellCheckingInspection", "ExtractMethodRecommender"})
public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
public class TapeRepoviewGeneratorPlugin extends PluginAdapter {
// 视图Repo包配置可通过配置文件自定义
private String slowQueryLoggerTime = "300";
@@ -27,7 +26,6 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
private String mapperPackage = "com.iqudoo.platform.application.database.mapper";
private String facadeViewRepositoryPackage = "com.iqudoo.platform.application.facade.repoview";
private String domainViewRepositoryPackage = "com.iqudoo.platform.application.domain.repoview";
private String domainViewRepositoryType = "Repository";
private String targetProject = "src/main/java";
// 1.4.1版本专用格式化器
@@ -47,14 +45,16 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
}
private void resolveConfiguration() {
targetProject = stringConfig("targetProject", targetProject);
slowQueryLoggerTime = stringConfig("slowQueryLoggerTime", slowQueryLoggerTime);
slowQueryLoggerLevel = stringConfig("slowQueryLoggerLevel", slowQueryLoggerLevel);
if (!UtilTools.inArray(new String[]{"error", "warn", "debug", "info"}, slowQueryLoggerLevel)) {
slowQueryLoggerLevel = "error";
}
facadeViewRepositoryPackage = stringConfig("facadeViewRepositoryPackage", facadeViewRepositoryPackage);
domainViewRepositoryPackage = stringConfig("domainViewRepositoryPackage", domainViewRepositoryPackage);
domainViewRepositoryType = stringConfig("domainViewRepositoryType", domainViewRepositoryType);
mapperPackage = stringConfig("mapperPackage", mapperPackage);
modelPackage = stringConfig("modelPackage", modelPackage);
mapperPackage = stringConfig("mapperPackage", mapperPackage);
targetProject = stringConfig("targetProject", targetProject);
}
private String stringConfig(String key, String defaultValue) {
@@ -71,31 +71,6 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
return defaultValue;
}
private String getSlowQueryLoggerLevel(IntrospectedTable introspectedTable) {
String level = getTableProperty(introspectedTable, "slowQueryLoggerLevel", slowQueryLoggerLevel);
if (!UtilTools.inArray(new String[]{"error", "warn", "debug", "info"}, level)) {
return "error";
}
return level;
}
private String getSlowQueryLoggerTime(IntrospectedTable introspectedTable) {
return getTableProperty(introspectedTable, "slowQueryLoggerTime", slowQueryLoggerTime);
}
// ====================== 统一 Table 属性获取你全类都能用 ======================
private String getTableProperty(IntrospectedTable introspectedTable, String propName, String defaultValue) {
try {
TableConfiguration tc = introspectedTable.getTableConfiguration();
String v = tc.getProperty(propName);
if (StringUtility.stringHasValue(v)) {
return v.trim();
}
} catch (Exception ignored) {
}
return defaultValue;
}
/**
* 核心方法仅为视图表生成 RepoView 代码
*/
@@ -110,8 +85,8 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
String domainObjectName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();
String exampleClassName = domainObjectName + "Example";
String mapperClassName = domainObjectName + "Mapper";
String repoInterfaceName = "I" + domainObjectName + domainViewRepositoryType;
String repoImplName = domainObjectName + domainViewRepositoryType + "Impl";
String repoInterfaceName = "I" + domainObjectName + "Repo";
String repoImplName = domainObjectName + "RepoImpl";
boolean hasBLOBColumns = ElementTools.hasBLOBColumns(introspectedTable);
@@ -127,7 +102,6 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
// 3. 生成视图Repo实现类
TopLevelClass repoImpl = generateRepoViewImpl(
introspectedTable,
repoImplName,
repoInterfaceName,
domainObjectName,
@@ -205,7 +179,6 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
* 生成视图Repo实现类逻辑不变仅适配新接口
*/
private TopLevelClass generateRepoViewImpl(
IntrospectedTable introspectedTable,
String implClassName,
String interfaceName,
String modelClassName,
@@ -214,8 +187,6 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
boolean hasBLOBColumns
) {
String tableName = introspectedTable.getFullyQualifiedTable().getIntrospectedTableName();
TopLevelClass implClass = new TopLevelClass(domainViewRepositoryPackage + "." + implClassName);
implClass.setVisibility(JavaVisibility.PUBLIC);
implClass.addAnnotation("@SuppressWarnings(\"DuplicatedCode\")");
@@ -243,10 +214,10 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
mapperField.addAnnotation("@Resource");
implClass.addField(mapperField);
generateFindOneMethod(implClass, tableName, modelClassName, exampleClassName);
generateGetListMethod(implClass, tableName, introspectedTable, modelClassName, exampleClassName, mapperFieldName, hasBLOBColumns);
generateCountMethod(implClass, tableName, introspectedTable, modelClassName, exampleClassName, mapperFieldName);
generateCountWithPageMethod(implClass, tableName, exampleClassName);
generateFindOneMethod(implClass, modelClassName, exampleClassName);
generateGetListMethod(implClass, modelClassName, exampleClassName, mapperFieldName, hasBLOBColumns);
generateCountMethod(implClass, modelClassName, exampleClassName, mapperFieldName);
generateCountWithPageMethod(implClass, modelClassName, exampleClassName, mapperFieldName);
return implClass;
}
@@ -274,7 +245,7 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
/**
* 生成findOne方法
*/
private void generateFindOneMethod(TopLevelClass implClass, String tableName, String modelClassName, String exampleClassName) {
private void generateFindOneMethod(TopLevelClass implClass, String modelClassName, String exampleClassName) {
Method method = new Method("findOne");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
@@ -299,7 +270,7 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
/**
* 生成getList方法
*/
private void generateGetListMethod(TopLevelClass implClass, String tableName, IntrospectedTable introspectedTable, String modelClassName, String exampleClassName, String mapperFieldName, boolean hasBLOBColumns) {
private void generateGetListMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName, boolean hasBLOBColumns) {
Method method = new Method("getList");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
@@ -322,7 +293,7 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
method.addBodyLine("result = " + mapperFieldName + ".selectByExample(example);");
}
method.addBodyLine("long useTime = new Date().getTime() - startTime;");
method.addBodyLine("if (useTime > " + getSlowQueryLoggerTime(introspectedTable) + ") {");
method.addBodyLine("if (useTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
@@ -330,12 +301,8 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("if (example.getLimitString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> limit: \" + example.getLimitString();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + getSlowQueryLoggerLevel(introspectedTable) + "(\"[SQL] select " + modelClassName + " view list long time\" +");
method.addBodyLine(" \"\\n\\t|-> table name: " + tableName + "\" +");
method.addBodyLine(" \"\\n\\t|-> use time: \" + useTime + \"ms\" +");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " view list use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + useTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
@@ -347,7 +314,7 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
/**
* 生成count方法
*/
private void generateCountMethod(TopLevelClass implClass, String tableName, IntrospectedTable introspectedTable, String modelClassName, String exampleClassName, String mapperFieldName) {
private void generateCountMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) {
Method method = new Method("count");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);
@@ -362,7 +329,7 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
method.addBodyLine("long startTime = new Date().getTime();");
method.addBodyLine("long count = " + mapperFieldName + ".countByExample(example);");
method.addBodyLine("long useTime = new Date().getTime() - startTime;");
method.addBodyLine("if (useTime > " + getSlowQueryLoggerTime(introspectedTable) + ") {");
method.addBodyLine("if (useTime > " + slowQueryLoggerTime + ") {");
method.addBodyLine("String exampleString = \"\";");
method.addBodyLine("if (example.getWhereString() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> where: \" + example.getWhereString();");
@@ -370,9 +337,8 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
method.addBodyLine("if (example.getOrderByClause() != null) {");
method.addBodyLine("exampleString += \"\\n\\t|-> order by: \" + example.getOrderByClause();");
method.addBodyLine("}");
method.addBodyLine("LOGGER." + getSlowQueryLoggerLevel(introspectedTable) + "(\"[SQL] select " + modelClassName + " view count long time\" +");
method.addBodyLine(" \"\\n\\t|-> table name: " + tableName + "\" +");
method.addBodyLine(" \"\\n\\t|-> use time: \" + useTime + \"ms\" +");
method.addBodyLine("LOGGER." + slowQueryLoggerLevel + "(\"[SQL] select " + modelClassName + " view count use long time\" +");
method.addBodyLine(" \"\\n\\t|-> use time\" + useTime + \"ms\" +");
method.addBodyLine(" exampleString +");
method.addBodyLine(" \"\\n\\t|-----------------------------------\"");
method.addBodyLine(");");
@@ -385,7 +351,7 @@ public class TapeViewRepositoryGeneratorPlugin extends PluginAdapter {
/**
* 生成count方法
*/
private void generateCountWithPageMethod(TopLevelClass implClass, String tableName, String exampleClassName) {
private void generateCountWithPageMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) {
Method method = new Method("countWithPage");
method.addAnnotation("@Override");
method.setVisibility(JavaVisibility.PUBLIC);