# mbg-code5 **Repository Path**: mnssnx/mbg-code5 ## Basic Information - **Project Name**: mbg-code5 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 1 - **Created**: 2022-04-10 - **Last Updated**: 2024-12-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 目的 1. 扩展mybatis-generator(mbg)自动生成接口,通过简单配置即可解决90%以上的数据库正删改查操作 2. 生成service层和controller层代码,为开发去除单一重复的代码搬运过程。 3. 整合了lombok、序列化接口和swagger功能,在springboot项目中,一键生成代码,启动项目,就可以在swagger测试界面进行测试; # 设计 为什么说通过简单配置即可解决90%以上的数据库正删改查操作? 1. 对于新增和修改,单条新增,批量新增,单条修改,批量修改四个接口就可以覆盖几乎所有新增和修改操作 2. 删除和查询都是通过设置过滤条件进行操作的,以查询为例; ### 第一类查询:通过字段等于操作,多个字段间通常是并且关系的查询 例1:字段1 = 某某值; 例2:字段1=某某值 并且 字段2 = 某某值 并且 字段n = 某某值 例3:不设置条件,查询全部数据 这类查询类似mbg原始提供的updateByPrimaryKeySelective动态修改字段接口 ### 第二类查询:通过外键字段查询和唯一索引字段查询 用户角色关联表(字段:id、user_id、role_id),user_id、role_id都是外键,user_id和role_id的组合是一个唯一索引; 对于用户角色关联表的查询: 1 通过user_id查询出一个列表数据 2 通过role_id查询出一个列表数据 3 通过user_id,role_id查询出一条数据 4 通过user_id集合查询出一个列表数据 5 通过role_id集合查询出一个列表数据 6 通过user_id,role_id集合查询出一个列表数据 7 通过role_id,user_id集合查询出一个列表数据 以上查询都是基于外键或外键的组合进行查询的,对于用户角色关联表来说以上查询肯定包含了绝大部分的查询场景; 小结: 1 对外键查询通常会有通过【外键字段】为条件去查询和【外键字段集合】为条件去查询; 2 唯一索引查询通常以唯一索引字段列表为条件去查询以及唯一索引列表前n-1个字段和第n个字段集合为查询条件查询;前n-1个字段和第n个字段集合:工资表有年度、月度、员工id为唯一索引,通常会有通过年度、月度、员工id集合去查询一批工资记录 第二类查询通过在generatorConfig.xml配置文件table标签指定外键或唯一索引,即可生成对应的接口 #### 第二类查询配置 - 单条查询(唯一索引查询): 配置 ``` ``` 接口 ``` /** * 唯一索引查询:通过roleId,userId查询 */ SysUserRole getByRoleIdUserId(@Param("roleId") String roleId, @Param("userId") String userId); ``` - 多条查询(外键查询): 配置 ``` ``` 接口 ``` /** * 多属性查询:通过roleId查询 */ List listByRoleId(@Param("roleId") String roleId); /** * 多属性查询:通过userId查询 */ List listByUserId(@Param("userId") String userId); ``` - 集合查询: 配置 ``` ``` 接口 ``` /** * 集合查询:通过roleIdCollection查询 */ List listByRoleIdCollection(@Param("roleIdCollection") Collection roleIdCollection); /** * 集合查询:通过userIdCollection查询 */ List listByUserIdCollection(@Param("userIdCollection") Collection userIdCollection); /** * 集合查询:通过roleId,userIdCollection查询 */ List listByRoleIdUserIdCollection(@Param("roleId") String roleId, @Param("userIdCollection") Collection userIdCollection); /** * 集合查询:通过userId,roleIdCollection查询 */ List listByUserIdRoleIdCollection(@Param("userId") String userId, @Param("roleIdCollection") Collection roleIdCollection); ``` 说明:接口配置不同接口用”;“分隔,同一接口不同参数用”,“分隔,例如集合查询配置:【role_id;user_id;role_id,user_id;user_id,role_id】,总共生成四个接口 分别是通过role_id集合查询、user_id集合查询、role_id和user_id集合查询、user_id和role_id集合查询(集合查询接口配置多个字段,最后一个字段是集合) ### 一键生成的service接口预览 ``` import com.mlx.advert.model.SysUserRole; import java.util.Collection; import java.util.List; public interface ISysUserRoleService { /** * 单条插入 */ int insert(SysUserRole data); /** * 批量插入;数据集合为空会报错,使用时请判断是否空 */ int insertBatch(Collection dataCollection); /** *批量修改:数据为空会报错,使用时请判断是否空 */ int updateBatch(Collection dataCollection); /** * 普通修改 */ int updateByPrimaryKey(SysUserRole data); /** * 动态修改 */ int updateByPrimaryKeySelective(SysUserRole data); /** * 主键删除 */ int deleteByPrimaryKey(String id); /** * id集合删除 */ int deleteByIdCollection(Collection idCollection); /** * 多条删除:通过roleId多条删除 */ int deleteByRoleId(String roleId); /** * 多条删除:通过userId多条删除 */ int deleteByUserId(String userId); /** * 多条删除:通过roleId,userId多条删除 */ int deleteByRoleIdUserId(String roleId, String userId); /** * 多条删除:通过userId,roleId多条删除 */ int deleteByUserIdRoleId(String userId, String roleId); /** * 集合删除:通过roleIdCollection删除 */ int deleteByRoleIdCollection(Collection roleIdCollection); /** * 集合删除:通过userIdCollection删除 */ int deleteByUserIdCollection(Collection userIdCollection); /** * 集合删除:通过roleId,userIdCollection删除 */ int deleteByRoleIdUserIdCollection(String roleId, Collection userIdCollection); /** * 集合删除:通过userId,roleIdCollection删除 */ int deleteByUserIdRoleIdCollection(String userId, Collection roleIdCollection); /** * 主键查询 */ SysUserRole selectByPrimaryKey(String id); /** * id集合查询 */ List selectByIdCollection(Collection idCollection); /** * 动态查询 */ List listSelective(SysUserRole query); /** * 唯一索引查询:通过roleId,userId查询 */ SysUserRole getByRoleIdUserId(String roleId, String userId); /** * 多属性查询:通过roleId查询 */ List listByRoleId(String roleId); /** * 多属性查询:通过userId查询 */ List listByUserId(String userId); /** * 集合查询:通过roleIdCollection查询 */ List listByRoleIdCollection(Collection roleIdCollection); /** * 集合查询:通过userIdCollection查询 */ List listByUserIdCollection(Collection userIdCollection); /** * 集合查询:通过roleId,userIdCollection查询 */ List listByRoleIdUserIdCollection(String roleId, Collection userIdCollection); /** * 集合查询:通过userId,roleIdCollection查询 */ List listByUserIdRoleIdCollection(String userId, Collection roleIdCollection); /** * 新增 */ int add(SysUserRole data); /** * 删除 */ int delete(String id); /** * 修改 */ int update(SysUserRole data); /** * 详情 */ SysUserRole detail(String id); /** * 列表查询 */ List listQuery(String roleId, String userId); } ``` # 使用 ### mbg-code下载地址 gitee地址:https://gitee.com/mnssnx/mbg-code5 gitee仓库地址:https://gitee.com/mnssnx/mbg-code5.git ### mbg-code安装 mbg-code安装到本地maven仓库:用idea打开项目,点击idea界面右侧maven,展开mbg-code,展开Lifecycle,点击install,右键运行安装命令 ### pom引入mbg-code 在build标签中plugins标签内添加插件 ``` org.mybatis.generator mybatis-generator-maven-plugin 1.3.6 src/main/resources/generatorConfig.xml true true mysql mysql-connector-java 8.0.22 com.mlx mbg-code 3.0.0 ``` ### generatorConfig.xml配置文件 #### 一个简化版配置 说明:这个配置默认整合了swagger、lombok,注意引入swagger、lombok依赖,启动类添加@EnableSwagger2注解 ```
``` #### 完整版配置 ```
``` # 源码简析 # 扩展方法类 所有的扩展方法类都在com.mlx.mbg.code.method包下,每个SQL接口都对应一个类,以selectByPrimaryKey接口为例 buildMethod方法和buildSQL方法分别是创建selectByPrimaryKey的java方法和对应sql语句的XML配置 ``` import com.mlx.mbg.code.MbgUtil; import com.mlx.mbg.code.inter.EnableMethod; import com.mlx.mbg.code.config.MbgPropertyConfig; import com.mlx.mbg.code.inter.ExtensionLevel; import com.mlx.mbg.code.inter.IExtensionMethod; import org.mybatis.generator.api.IntrospectedColumn; import org.mybatis.generator.api.IntrospectedTable; import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType; import org.mybatis.generator.api.dom.java.JavaVisibility; import org.mybatis.generator.api.dom.java.Method; import org.mybatis.generator.api.dom.java.Parameter; import org.mybatis.generator.api.dom.xml.Attribute; import org.mybatis.generator.api.dom.xml.TextElement; import org.mybatis.generator.api.dom.xml.XmlElement; import java.util.ArrayList; import java.util.Collections; import java.util.List; @EnableMethod public class SelectByPrimaryKeyMethod implements IExtensionMethod { @Override public boolean checkConfig(ExtensionLevel level, MbgPropertyConfig mbgPropertyConfig, IntrospectedTable introspectedTable) { return getExtensionLevelSet().contains(level) && MbgUtil.checkPrimaryKey(introspectedTable, "selectByPrimaryKey"); } @Override public int getOrder() { return 1000; } @Override public List buildMethod(IntrospectedTable introspectedTable) { String tableName = introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime(); List keyColumnList = introspectedTable.getPrimaryKeyColumns(); JavaVisibility methodVisibility = JavaVisibility.PUBLIC; FullyQualifiedJavaType methodReturnType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()); String methodName = "selectByPrimaryKey"; List methodParameterList = new ArrayList(); for (IntrospectedColumn column : keyColumnList){ String javaProperty = column.getJavaProperty(); FullyQualifiedJavaType javaType = column.getFullyQualifiedJavaType(); // 参数类型 FullyQualifiedJavaType methodParameterType = new FullyQualifiedJavaType(javaType.getShortName()); // 方法参数 Parameter methodParameter = new Parameter(methodParameterType, javaProperty); methodParameterList.add(methodParameter); } Method method = new Method(); method.setVisibility(methodVisibility); method.setReturnType(methodReturnType); method.setName(methodName); for (Parameter parameter : methodParameterList){ method.addParameter(parameter); } method.addJavaDocLine("/**"); method.addJavaDocLine(" * 主键查询"); method.addJavaDocLine(" */"); return Collections.singletonList(method); } @Override public List buildSQL(IntrospectedTable introspectedTable) { String tableName = introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime(); List keyColumnList = introspectedTable.getPrimaryKeyColumns(); XmlElement selectByPrimaryKey = new XmlElement("select"); selectByPrimaryKey.addAttribute(new Attribute("id", "selectByPrimaryKey")); selectByPrimaryKey.addAttribute(new Attribute("resultMap", "BaseResultMap")); XmlElement baseInclude = new XmlElement("include"); baseInclude.addAttribute(new Attribute("refid", "Base_Column_List")); StringBuilder stringBuilder = new StringBuilder("where "); int size = keyColumnList.size(); int maxIndex = size - 1; for (int i = 0; i < size ; i++){ IntrospectedColumn column = keyColumnList.get(0); String actualColumnName = column.getActualColumnName(); String javaProperty = column.getJavaProperty(); String jdbcTypeName = column.getJdbcTypeName(); if (i != maxIndex){ stringBuilder.append(actualColumnName).append(" = ").append("#{").append(javaProperty).append(",jdbcType=").append(jdbcTypeName).append("} and "); }else { stringBuilder.append(actualColumnName).append(" = ").append("#{").append(javaProperty).append(",jdbcType=").append(jdbcTypeName).append("}"); } } selectByPrimaryKey.addElement(new TextElement("select")); selectByPrimaryKey.addElement(baseInclude); selectByPrimaryKey.addElement(new TextElement("from " + introspectedTable.getFullyQualifiedTableNameAtRuntime())); selectByPrimaryKey.addElement(new TextElement(stringBuilder.toString())); return Collections.singletonList(selectByPrimaryKey); } } ``` ### 插件核心类MbgPlugin 完整类名:com.mlx.mbg.code.plugin.MbgPlugin 6个核心方法 1. 生成实体类的方法:modelBaseRecordClassGenerated(TopLevelClass interfaze, IntrospectedTable introspectedTable) 2. 生成XML配置文件方法:sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) 3. 生成mapper的java文件方法:clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) 4. 生成service的java文件方法: generateServiceInterface(IntrospectedTable introspectedTable) 5. 生成serviceImpl的java文件方法:generateServiceImpl(IntrospectedTable introspectedTable) 6. 生成controller的java文件方法:generateController(IntrospectedTable introspectedTable) 插件运行时,先读取并实例化method包下的所有方法类,XML配置文件方法遍历执行所有方法类的buildSQL方法生成对应的xml配置, java文件方法遍历执行所有方法类的buildMethod方法,生成对应的java方法;此时所有的java方法并没有方法体;如果是serviceImpl文件或controller文件,还需要添加方法体 generateServiceImpl方法部分阅览: ``` // 获取扩展方法 List methodList = new ArrayList<>(); for (IExtensionMethod extensionMethod : extensionMethodList){ if (extensionMethod.checkConfig(ExtensionLevel.SERVICE, mbgPropertyConfig, introspectedTable)){ methodList.addAll(extensionMethod.buildMethod(introspectedTable)); } } // 遍历扩展方法,添加@Override注解并加入serviceImpl文件中 for (Method method : methodList){ List parameters = method.getParameters(); for (Parameter parameter : parameters){ importedTypes.add(parameter.getType()); } method.addAnnotation("@Override"); List javaDocLines = method.getJavaDocLines(); if (MbgUtil.isNotEmpty(javaDocLines)){ javaDocLines.clear(); } serviceImplInterface.addMethod(method); } // 添加方法体 for (Method method : serviceImplInterface.getMethods()) { List parameters = method.getParameters(); StringBuilder paramBuilder = new StringBuilder(); if (MbgUtil.isNotEmpty(parameters)){ for (Parameter p : parameters){ paramBuilder.append(p.getName()).append(", "); } paramBuilder.deleteCharAt(paramBuilder.length() - 1); paramBuilder.deleteCharAt(paramBuilder.length() - 1); } String methodName = method.getName(); if (methodName.equals("add")){ method.addBodyLine("return "+ mapperFieldName +"." + "insert" + "(" + paramBuilder + ");"); }else if (methodName.equals("delete")){ method.addBodyLine("return "+ mapperFieldName +"." + "deleteByPrimaryKey" + "(" + paramBuilder + ");"); }else if (methodName.equals("update")){ method.addBodyLine("return "+ mapperFieldName +"." + "updateByPrimaryKeySelective" + "(" + paramBuilder + ");"); }else if (methodName.equals("detail")){ method.addBodyLine("return "+ mapperFieldName +"." + "selectByPrimaryKey" + "(" + paramBuilder + ");"); }else { method.addBodyLine("return "+ mapperFieldName +"." + method.getName() + "(" + paramBuilder + ");"); } } ``` ### 总结 每个开发者的开发习惯不同,SQL接口扩展的思路也不同,每个开发者应该有一个属于自己的mbg扩展插件。源码简析希望能给未接触过mbg自定义扩展接口的开发者一个入门的机会。愿您能够扩展出适应自己开发习惯的mbg插件