diff --git a/README.md b/README.md index 9dc06f0..f2f8772 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ ### 2. 在 `mybatis.generator.xml` 中配置插件 ```xml + @@ -135,10 +136,13 @@ - - - - + + + + + + + @@ -157,22 +161,41 @@ ### 3. 配置参数说明 -| 参数名 | 说明 | 默认值 | 必需 | -|----------------------------|-------------------------------|-------------------------------------------------------|------| -| `targetProject` | 生成代码的目标项目路径 | `src/main/java` | 否 | -| `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` | 否 | +| 参数名 | 说明 | 默认值 | 必需 | +|----------------------------|-------------------------------|---------------------------------------------------------|------| +| `targetProject` | 生成代码的目标项目路径 | `src/main/java` | 否 | +| `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()` | 否 | -| `slowQueryLoggerTime` | 慢查询日志时间阈值 | `300` | 否 | -| `slowQueryLoggerLevel` | 慢查询日志类型:error,warn,debug,info | `error` | 否 | -| `priorityPrimaryKeyOffset` | 优先查询主键偏移阈值 | `0` | 否 | -| `ignorePageSize` | 忽略分页阈值 | `10000` | 否 | -| `startPageNum` | 分页开始页码 | `1` | 否 | -| `maxPageSize` | 最大每页数量 | `100` | 否 | +| `snowflakeUtilGenId` | 雪花算法ID生成方法 | `SnowflakeUtil.nextId()` | 否 | +| `changeLogContextClassPackage` | 变更日志上下文包路径 | `com.iqudoo.platform.application.domain.changeLog` | 否 | +| `changeLogContextClassName` | 变更日志上下文类 | `ChangeLogContext` | 否 | +| `changeLogEnable` | 变更日志监听开关 | `false` | 否 | +| `slowQueryLoggerTime` | 慢查询日志时间阈值 | `300` | 否 | +| `slowQueryLoggerLevel` | 慢查询日志类型:error,warn,debug,info | `error` | 否 | +| `priorityPrimaryKeyOffset` | 优先查询主键偏移阈值 | `0` | 否 | +| `ignorePageSize` | 忽略分页阈值 | `10000` | 否 | +| `startPageNum` | 分页开始页码 | `1` | 否 | +| `maxPageSize` | 最大每页数量 | `100` | 否 | + +## 变更日志监听 + +ChangeLogContext应该提供以下实现的静态方法,供Repository实现中调用 +```java +public class ChangeLogContext { + + /** + * 添加一条变更 + */ + public static void addLog(String tableName, String eventType, Long dataGuid, Map fieldChanges) { + // your thing code + } + +} +``` ## 数据库表结构要求 diff --git a/releases/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar b/releases/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar index 456df45..8732b42 100644 Binary files a/releases/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar and b/releases/tape-mybatis-generator-plugin-1.0-SNAPSHOT.jar differ diff --git a/src/main/java/com/iqudoo/framework/mybatis/TapeRepositoryGeneratorPlugin.java b/src/main/java/com/iqudoo/framework/mybatis/TapeRepositoryGeneratorPlugin.java index 8b11df8..ae16b6f 100644 --- a/src/main/java/com/iqudoo/framework/mybatis/TapeRepositoryGeneratorPlugin.java +++ b/src/main/java/com/iqudoo/framework/mybatis/TapeRepositoryGeneratorPlugin.java @@ -18,12 +18,15 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { // 固定配置项 private String slowQueryLoggerTime = "300"; - private String priorityPrimaryKeyOffset = "0"; private String slowQueryLoggerLevel = "error"; - private String snowflakeUtilClass = "com.iqudoo.framework.tape.modules.utils.SnowflakeUtil"; - private String snowflakeUtilGenId = "SnowflakeUtil.nextId()"; + private String priorityPrimaryKeyOffset = "0"; 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 modelPackage = "com.iqudoo.platform.application.database.model"; private String mapperPackage = "com.iqudoo.platform.application.database.mapper"; private String targetProject = "src/main/java"; @@ -47,19 +50,33 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { private void resolveConfiguration() { slowQueryLoggerTime = stringConfig("slowQueryLoggerTime", slowQueryLoggerTime); slowQueryLoggerLevel = stringConfig("slowQueryLoggerLevel", slowQueryLoggerLevel); - priorityPrimaryKeyOffset = stringConfig("priorityPrimaryKeyOffset", priorityPrimaryKeyOffset); if (!UtilTools.inArray(new String[]{"error", "warn", "debug", "info"}, slowQueryLoggerLevel)) { slowQueryLoggerLevel = "error"; } - snowflakeUtilClass = stringConfig("snowflakeUtilClass", snowflakeUtilClass); - snowflakeUtilGenId = stringConfig("snowflakeUtilGenId", snowflakeUtilGenId); + priorityPrimaryKeyOffset = stringConfig("priorityPrimaryKeyOffset", priorityPrimaryKeyOffset); + 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); modelPackage = stringConfig("modelPackage", modelPackage); mapperPackage = stringConfig("mapperPackage", mapperPackage); targetProject = stringConfig("targetProject", targetProject); } + 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)) { @@ -373,13 +390,13 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { implClass.addField(mapperField); // 原有方法生成逻辑(无修改) - generateFindAnyByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName); - generateFindValidByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName); - generateFindTrashByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName); + generateFindAnyByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName, hasBLOBColumns); + generateFindValidByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName, hasBLOBColumns); + generateFindTrashByIdMethod(implClass, modelClassName, mapperFieldName, exampleClassName, hasBLOBColumns); generateInsertMethod(implClass, modelClassName, mapperFieldName, introspectedTable); generateBatchInsertMethod(implClass, modelClassName, mapperFieldName, introspectedTable); generateUpdateMethod(implClass, modelClassName, exampleClassName, mapperFieldName, introspectedTable, hasBLOBColumns); - generateUpdateByExampleSelectiveMethod(implClass, modelClassName, exampleClassName, mapperFieldName); + generateUpdateByExampleSelectiveMethod(implClass, modelClassName, exampleClassName, mapperFieldName, introspectedTable, hasBLOBColumns); generateDeleteByIdMethod(implClass, modelClassName, mapperFieldName); generateDeleteAllMethod(implClass, modelClassName, exampleClassName, mapperFieldName); generateTrashByIdMethod(implClass, modelClassName, mapperFieldName); @@ -399,71 +416,6 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { } // ------------------------ 所有原有方法(无修改) ------------------------ - 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("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); - method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(1);"); - method.addBodyLine("}"); - method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();"); - method.addBodyLine(lowerFirst(modelClassName) + ".setIsDelete(1);"); - method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); - - implClass.addMethod(method); - } - - private void generateTrashAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { - Method method = new Method("trashAll"); - method.addAnnotation("@Override"); - method.setVisibility(JavaVisibility.PUBLIC); - method.setReturnType(new FullyQualifiedJavaType("int")); - method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); - method.addException(new FullyQualifiedJavaType("Throwable")); - - // 方法体 - method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); - method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(0);"); - method.addBodyLine("}"); - method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();"); - method.addBodyLine(lowerFirst(modelClassName) + ".setIsHidden(1);"); - method.addBodyLine(lowerFirst(modelClassName) + ".setDeleteToken(" + lowerFirst(modelClassName) + ".getGuid() + \"\");"); - method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); - - implClass.addMethod(method); - } - - private void generateRecoverAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { - Method method = new Method("recoverAll"); - method.addAnnotation("@Override"); - method.setVisibility(JavaVisibility.PUBLIC); - method.setReturnType(new FullyQualifiedJavaType("int")); - method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); - method.addException(new FullyQualifiedJavaType("Throwable")); - - // 方法体 - method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); - method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(1);"); - method.addBodyLine("}"); - method.addBodyLine(modelClassName + " " + lowerFirst(modelClassName) + " = new " + modelClassName + "();"); - method.addBodyLine(lowerFirst(modelClassName) + ".setIsHidden(0);"); - method.addBodyLine(lowerFirst(modelClassName) + ".setDeleteToken(\"VALID\");"); - method.addBodyLine(lowerFirst(modelClassName) + ".setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); - - implClass.addMethod(method); - } private void generateJavaFileToDisk(Interface anInterface, String packageName) { try { @@ -507,7 +459,10 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { } private void addImportPackages(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperClassName, String interfaceName) { - implClass.addImportedType(new FullyQualifiedJavaType(snowflakeUtilClass)); + 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)); @@ -516,56 +471,80 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { 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.ArrayList")); - implClass.addImportedType(new FullyQualifiedJavaType("java.util.Date")); - implClass.addImportedType(new FullyQualifiedJavaType("java.util.List")); + implClass.addImportedType(new FullyQualifiedJavaType("java.util.*")); } - private void generateFindAnyByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName) { + private void generateFindAnyByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName, boolean hasBLOBColumns) { Method method = new Method("findAnyById"); method.addAnnotation("@Override"); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(new FullyQualifiedJavaType(modelClassName)); - method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id")); + method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "guid")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = " + mapperFieldName + ".selectByPrimaryKey(id);"); - method.addBodyLine("if (aDo != null && aDo.getIsDelete() == 1) {"); - method.addBodyLine("return null;"); + 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 generateFindValidByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName) { + private void generateFindValidByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName, boolean hasBLOBColumns) { Method method = new Method("findValidById"); method.addAnnotation("@Override"); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(new FullyQualifiedJavaType(modelClassName)); - method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id")); + method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "guid")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = " + mapperFieldName + ".selectByPrimaryKey(id);"); - method.addBodyLine("if (aDo != null && (aDo.getIsDelete() == 1 || aDo.getIsHidden() == 1)) {"); - method.addBodyLine("return null;"); + method.addBodyLine(modelClassName + " aDo = null;"); + method.addBodyLine(exampleClassName + " example"); + method.addBodyLine(" = new " + exampleClassName + "();"); + method.addBodyLine("example.createCriteria()"); + method.addBodyLine(" .andIsHiddenEqualTo(0)"); + 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 generateFindTrashByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName) { + private void generateFindTrashByIdMethod(TopLevelClass implClass, String modelClassName, String mapperFieldName, String exampleClassName, boolean hasBLOBColumns) { Method method = new Method("findTrashById"); method.addAnnotation("@Override"); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(new FullyQualifiedJavaType(modelClassName)); - method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "id")); + method.addParameter(new Parameter(new FullyQualifiedJavaType("long"), "guid")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = " + mapperFieldName + ".selectByPrimaryKey(id);"); - method.addBodyLine("if (aDo != null && (aDo.getIsDelete() == 1 || aDo.getIsHidden() == 0)) {"); - method.addBodyLine("return null;"); + method.addBodyLine(modelClassName + " aDo = null;"); + method.addBodyLine(exampleClassName + " example"); + method.addBodyLine(" = new " + exampleClassName + "();"); + method.addBodyLine("example.createCriteria()"); + method.addBodyLine(" .andIsHiddenEqualTo(1)"); + 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;"); @@ -584,7 +563,7 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("if (record.getGuid() != null) {"); method.addBodyLine("aDo.setGuid(record.getGuid());"); method.addBodyLine("} else {"); - method.addBodyLine("Long guid = " + snowflakeUtilGenId + ";"); + method.addBodyLine("Long guid = " + guidGeneratorCode + ";"); method.addBodyLine("aDo.setGuid(guid);"); method.addBodyLine("}"); @@ -610,16 +589,18 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("int count = " + mapperFieldName + ".insert(aDo);"); method.addBodyLine("if (count > 0) {"); - method.addBodyLine("return aDo;"); - method.addBodyLine("}"); - 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());"); - method.addBodyLine("throw new Throwable(\"Database insert failed, " + modelClassName + ": \" + aDo);"); - + 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); } @@ -640,7 +621,7 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("if (record.getGuid() != null) {"); method.addBodyLine("aDo.setGuid(record.getGuid());"); method.addBodyLine("} else {"); - method.addBodyLine("Long guid = " + snowflakeUtilGenId + ";"); + method.addBodyLine("Long guid = " + guidGeneratorCode + ";"); method.addBodyLine("aDo.setGuid(guid);"); method.addBodyLine("}"); @@ -648,7 +629,6 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { String fieldName = column.getJavaProperty(); String setterMethod = "set" + upperFirst(fieldName); String getterMethod = "get" + upperFirst(fieldName); - if ("guid".equals(fieldName) || "isDelete".equals(fieldName) || "isHidden".equals(fieldName) || "deleteToken".equals(fieldName) || "dataVersion".equals(fieldName) || "createTime".equals(fieldName) || "updateTime".equals(fieldName)) { @@ -668,9 +648,15 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("int count = " + mapperFieldName + ".batchInsert(batch);"); method.addBodyLine("if (count == batch.size()) {"); + if (isChangeLogEnable()) { + method.addBodyLine("for (" + modelClassName + " aDo : batch) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"batchInsert\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + } method.addBodyLine("return batch;"); method.addBodyLine("}"); - method.addBodyLine("throw new Throwable(\"Database batchInsert failed, " + modelClassName + " affected: \" + count + \", expected: \" + batch.size());"); + method.addBodyLine("throw new Throwable(\"Batch insert failed, " + modelClassName + " affected: \" + count + \", expected: \" + batch.size());"); implClass.addMethod(method); } @@ -684,26 +670,32 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addParameter(new Parameter(new FullyQualifiedJavaType(modelClassName), "record")); method.addException(new FullyQualifiedJavaType("Throwable")); - method.addBodyLine(modelClassName + " aDo = findValidById(record.getGuid());"); - method.addBodyLine("if (aDo == null) {"); - method.addBodyLine("throw new Throwable(\"Database record not found, " + modelClassName + " GUID:\" + record.getGuid());"); + 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 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) || "isHidden".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 + "();"); method.addBodyLine("Integer lockDataVersion = record.getDataVersion();"); method.addBodyLine("if (lockDataVersion == null) {"); @@ -717,14 +709,22 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("// update data version"); method.addBodyLine("record.setDataVersion(aDo.getDataVersion());"); method.addBodyLine("record.setUpdateTime(aDo.getUpdateTime());"); - String updateMethod = hasBLOBColumns ? "updateByExampleWithBLOBs" : "updateByExample"; - method.addBodyLine("return " + mapperFieldName + "." + updateMethod + "(aDo, updateWhere);"); - + 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) { + 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); @@ -737,15 +737,62 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(0);"); method.addBodyLine("}"); - method.addBodyLine("record.setUpdateTime(new Date());"); + + method.addBodyLine("List 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(" .andIsHiddenEqualTo(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> diffGroup = new HashMap<>();"); + method.addBodyLine("for (" + modelClassName + " aDo : recordList) {"); + method.addBodyLine("Map 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) || "isHidden".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.setIsHidden(null);"); method.addBodyLine("record.setIsDelete(null);"); method.addBodyLine("record.setDeleteToken(null);"); - method.addBodyLine("record.setDataVersion(null);"); method.addBodyLine("record.setCreateTime(null);"); - method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(record, example);"); - + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByExampleSelective(record, example);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine("for (Map.Entry> 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); } @@ -757,7 +804,6 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { 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 = findValidById(id);"); method.addBodyLine("if (aDo == null) {"); method.addBodyLine("return 0;"); @@ -767,8 +813,14 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("}"); method.addBodyLine("aDo.setIsDelete(1);"); method.addBodyLine("aDo.setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); - + 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); } @@ -787,8 +839,16 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("aDo.setIsHidden(1);"); method.addBodyLine("aDo.setDeleteToken(aDo.getGuid() + \"\");"); method.addBodyLine("aDo.setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); - + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"trashById\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + } implClass.addMethod(method); } @@ -810,7 +870,124 @@ public class TapeRepositoryGeneratorPlugin extends PluginAdapter { method.addBodyLine("aDo.setIsHidden(0);"); method.addBodyLine("aDo.setDeleteToken(\"VALID\");"); method.addBodyLine("aDo.setUpdateTime(new Date());"); - method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + if (isChangeLogEnable()) { + method.addBodyLine("int update = " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + method.addBodyLine("if (update > 0) {"); + method.addBodyLine(changeLogContextClassName + ".addLog(\"" + modelClassName + "\","); + method.addBodyLine(" \"recoverById\", aDo.getGuid(), new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByPrimaryKey(aDo);"); + } + 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("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); + method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(1);"); + method.addBodyLine("}"); + method.addBodyLine("List 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 generateTrashAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { + Method method = new Method("trashAll"); + method.addAnnotation("@Override"); + method.setVisibility(JavaVisibility.PUBLIC); + method.setReturnType(new FullyQualifiedJavaType("int")); + method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); + method.addException(new FullyQualifiedJavaType("Throwable")); + + // 方法体 + method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); + method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(0);"); + method.addBodyLine("}"); + method.addBodyLine("List 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) + ".setIsHidden(1);"); + method.addBodyLine(lowerFirst(modelClassName) + ".setDeleteToken(" + guidGeneratorCode + " + \"\");"); + 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(" \"trashAll\", 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 generateRecoverAllMethod(TopLevelClass implClass, String modelClassName, String exampleClassName, String mapperFieldName) { + Method method = new Method("recoverAll"); + method.addAnnotation("@Override"); + method.setVisibility(JavaVisibility.PUBLIC); + method.setReturnType(new FullyQualifiedJavaType("int")); + method.addParameter(new Parameter(new FullyQualifiedJavaType(exampleClassName), "example")); + method.addException(new FullyQualifiedJavaType("Throwable")); + + // 方法体 + method.addBodyLine("for (" + exampleClassName + ".Criteria criteria : example.getOredCriteria()) {"); + method.addBodyLine("criteria.andIsDeleteEqualTo(0).andIsHiddenEqualTo(1);"); + method.addBodyLine("}"); + method.addBodyLine("List 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) + ".setIsHidden(0);"); + method.addBodyLine(lowerFirst(modelClassName) + ".setDeleteToken(\"VALID\");"); + 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(" \"recoverAll\", guid, new HashMap<>());"); + method.addBodyLine("}"); + method.addBodyLine("}"); + method.addBodyLine("return update;"); + } else { + method.addBodyLine("return " + mapperFieldName + ".updateByExampleSelective(" + lowerFirst(modelClassName) + ", example);"); + } implClass.addMethod(method); } diff --git a/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java b/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java index 7bcd3e7..214f505 100644 --- a/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java +++ b/src/main/java/com/iqudoo/framework/mybatis/TapeRepoviewGeneratorPlugin.java @@ -22,10 +22,10 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { // 视图Repo包配置(可通过配置文件自定义) private String slowQueryLoggerTime = "300"; private String slowQueryLoggerLevel = "error"; - private String facadeRepoviewPackage = "com.iqudoo.platform.application.facade.repoview"; - private String domainRepoviewPackage = "com.iqudoo.platform.application.domain.repoview"; private String modelPackage = "com.iqudoo.platform.application.database.model"; 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 targetProject = "src/main/java"; // 1.4.1版本专用格式化器 @@ -50,8 +50,8 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { if (!UtilTools.inArray(new String[]{"error", "warn", "debug", "info"}, slowQueryLoggerLevel)) { slowQueryLoggerLevel = "error"; } - facadeRepoviewPackage = stringConfig("facadeRepoviewPackage", facadeRepoviewPackage); - domainRepoviewPackage = stringConfig("domainRepoviewPackage", domainRepoviewPackage); + facadeViewRepositoryPackage = stringConfig("facadeRepoviewPackage", facadeViewRepositoryPackage); + domainViewRepositoryPackage = stringConfig("domainRepoviewPackage", domainViewRepositoryPackage); modelPackage = stringConfig("modelPackage", modelPackage); mapperPackage = stringConfig("mapperPackage", mapperPackage); targetProject = stringConfig("targetProject", targetProject); @@ -118,8 +118,8 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { generatedJavaFiles.add(implFile); // 4. 手动写入磁盘(兼容1.4.1) - generateJavaFileToDisk(repoInterface, facadeRepoviewPackage); - generateJavaFileToDisk(repoImpl, domainRepoviewPackage); + generateJavaFileToDisk(repoInterface, facadeViewRepositoryPackage); + generateJavaFileToDisk(repoImpl, domainViewRepositoryPackage); return generatedJavaFiles; } @@ -128,7 +128,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { * 核心修改:生成视图Repo接口(移除继承,手动添加指定方法) */ private Interface generateRepoViewInterface(String interfaceName, String modelClassName, String exampleClassName) { - Interface repoInterface = new Interface(facadeRepoviewPackage + "." + interfaceName); + Interface repoInterface = new Interface(facadeViewRepositoryPackage + "." + interfaceName); repoInterface.setVisibility(JavaVisibility.PUBLIC); // 添加必要的导入包(仅保留model、example、List) @@ -187,7 +187,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { boolean hasBLOBColumns ) { - TopLevelClass implClass = new TopLevelClass(domainRepoviewPackage + "." + implClassName); + TopLevelClass implClass = new TopLevelClass(domainViewRepositoryPackage + "." + implClassName); implClass.setVisibility(JavaVisibility.PUBLIC); implClass.addAnnotation("@SuppressWarnings(\"DuplicatedCode\")"); implClass.addAnnotation("@Repository"); @@ -196,7 +196,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { addImportPackages(implClass, modelClassName, exampleClassName, mapperClassName, interfaceName); // 实现Repo接口 - FullyQualifiedJavaType superInterface = new FullyQualifiedJavaType(facadeRepoviewPackage + "." + interfaceName); + FullyQualifiedJavaType superInterface = new FullyQualifiedJavaType(facadeViewRepositoryPackage + "." + interfaceName); implClass.addSuperInterface(superInterface); // slow query logger @@ -232,7 +232,7 @@ public class TapeRepoviewGeneratorPlugin extends PluginAdapter { implClass.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + modelClassName)); implClass.addImportedType(new FullyQualifiedJavaType(modelPackage + "." + exampleClassName)); // Repo接口 - implClass.addImportedType(new FullyQualifiedJavaType(facadeRepoviewPackage + "." + interfaceName)); + implClass.addImportedType(new FullyQualifiedJavaType(facadeViewRepositoryPackage + "." + interfaceName)); // 注解&工具类 implClass.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Repository")); implClass.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));