# EasyCode **Repository Path**: DaHuYuXiXi/easy-code ## Basic Information - **Project Name**: EasyCode - **Description**: 轻量级代码自动生成器 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 5 - **Forks**: 0 - **Created**: 2022-11-29 - **Last Updated**: 2023-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Easy-Generator ## 介绍 本项目是一个轻量级代码生成器,并提供多种方式来完成模拟数据的批量生产,项目架构如下: ![img_3.png](img_3.png) **** ## 代码生成 ### 常用注解 - @Table: 指明表名和表注释 ```java @Retention(RetentionPolicy.RUNTIME) @Target(value = ElementType.TYPE) public @interface Table { /** * 不指定默认为类名小写转驼峰 */ String value() default ""; /** * 注释 */ String comment() default ""; /** * 是否需要添加dropTable语句 */ boolean dropTable() default true; } ``` - @Field: 指明当前表中某个字段的相关信息 ```java @Retention(RetentionPolicy.RUNTIME) @Target(value = ElementType.FIELD) public @interface Filed { /** * 字段名: 不指定,默认为属性名小写转驼峰 */ String value() default ""; /** * 字段类型,可以手动指定确切的字段类型,否则采用内定的java-sql类型转换过程 */ String filedType() default ""; /** * 字段长度,对于varchar,char等类型来说是需要进行设置的,这里也可以不进行设置,会给予默认大小 */ String filedLen() default ""; /** * 字段是否为notNull,默认为true */ boolean notNull() default true; /** * 字段是否自增 */ boolean autoIncrement() default false; /** * 字段默认值 */ String defaultVal() default ""; /** * 字段描述 */ String comment() default ""; /** * 字段顺序,值越小优先级越大 */ int order() default Integer.MAX_VALUE; } ``` - @Key: ```java @Retention(RetentionPolicy.RUNTIME) @Target(value = ElementType.FIELD) public @interface Key { /** * 索引类型 */ String keyType() default ""; /** * 索引名 */ String keyName() default ""; /** * 索引列: 默认包含当前字段,通过该属性指定其他字段---对于复合索引而言 */ String[] otherColumns() default {}; //-----------外键索引---------------- /** * 主表名 */ String masterTableName() default ""; /** * 受约束的主表字段 */ String[] masterColumns() default {}; /** * 受约束从表字段: 默认包含当前字段,通过该属性指定其他字段,对于复合索引而言 */ String[] otherSalveColumns() default {}; } ``` **** ### 基本使用 ```java @Table(comment = "学生表",value = "stu") public class Stu { @Key(keyType = KeyTypeConstants.PRIMARY_KEY) @Filed(comment = "主键id", autoIncrement = true) private Integer id; @Filed(comment = "姓名") private String name; @Filed(comment = "年龄") private Integer age; @Filed(comment = "就读大学") private String university; @Filed(comment = "主攻编程语言") private String language; @Filed(comment = "老家城市") private String homeCity; @Filed(comment = "电话号码") private String phoneNumber; @Filed(comment = "qq邮箱号码") private String qqEamil; @Filed(comment = "出生日期") private LocalDateTime birthTime; @Filed(comment = "创建时间", defaultVal = DefaultValConstants.CURRENT_TIMESTAMP) private LocalDateTime createTime; @Filed(comment = "修改时间", defaultVal = DefaultValConstants.CURRENT_TIMESTAMP) private LocalDateTime updateTime; } ``` - 测试 ```java @SpringBootTest public class ExampleOneTest { @Autowired private SqlManager sqlManager; @Test public void test() { sqlManager.addMappedObject(new Stu()); sqlManager.build(); } } ``` 生成的模板文件(如果不满意,可以自定义模板,后面会介绍): ![img_4.png](img_4.png) 产生的建库语句: ```sql CREATE DATABASE IF NOT EXISTS `test`; USE `test`; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; DROP TABLE IF EXISTS `stu`; CREATE TABLE `stu` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名', `age` int NOT NULL COMMENT '年龄', `university` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '就读大学', `language` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主攻编程语言', `home_city` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '老家城市', `phone_number` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '电话号码', `qq_eamil` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'qq邮箱号码', `birth_time` datetime NOT NULL COMMENT '出生日期', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (id) ) ENGINE = InnoDB AUTO_INCREMENT = 0 CHARACTER SET = 'utf8' COLLATE = 'utf8_general_ci' ROW_FORMAT = compact COMMENT '学生表'; SET FOREIGN_KEY_CHECKS = 1; ``` **** ## 全局控制属性 通过全局属性控制,我们可以控制SqlManager的一些行为: ```java @ConfigurationProperties(prefix ="easy-generator.global") @Data @Slf4j public class GlobalProperties { /** * sqlExecer需要执行sqlBuilder产生的sql吗? ---> 默认是不执行的 */ private Boolean execSql=Boolean.FALSE; /** * 需要生成模板文件吗? ---> 默认是生成的 */ private Boolean sqlMapper=Boolean.TRUE; /** * 是否需要生成模拟数据 ---> 默认不生成,我们需要手动开启 */ private Boolean mockData=Boolean.FALSE; /** * 是否将sqlBuilder生成的sql打印到文件保存 --> 默认会保存 */ private Boolean printSqlToFile=Boolean.TRUE; /** * sql文件输出到哪里保存呢? --> 默认是当前项目路径下 */ private String sqlFilePath; /** * 是否需要添加创建数据库的sql语句,如果数据库不存在则进行创建 --> 默认会添加 */ private Boolean createDataBase=Boolean.TRUE; /** * 是否从已经存在的表中进行信息导入 ---> 默认不开启,如果开启了从已经存在的表导入,那么将会忽略对应的JavaBean对象解析 */ private Boolean importFromExistTable=Boolean.FALSE; /** * 已经存在的表名,如果存在多个,用逗号分隔开来 */ private String existTableName; /** * 特性管理(后面介绍) */ private Feature feature=Feature.newDefaultFeature(); } ``` 具体每个属性什么含义,大家可以参考上面的注释,外加自己进行一番测试。 **** ## 模板文件相关属性 ```java /** * 最终每个文件的输出路径为: projectPath+basePackageModuleName+对应模块名+文件名 */ @ConfigurationProperties(prefix = "easy-generator.template") @Data public class SqlTemplateConfig { /** * 项目路径 */ private String projectPath=""; /** * 基础包模块: 可以不指定 */ private String basePackageModuleName=""; /** * entity模块名: 该模块下又分为 dao,dto,vo,factory */ private String entityModuleName="entity"; /** * dao层下存放直接与数据库交互的模型 */ private String daoModuleName= ModuleUtil.merge(entityModuleName,"dao"); /** * dto层下存放响应给前端的结果对象 */ private String dtoModuleName=ModuleUtil.merge(entityModuleName,"dto"); /** * vo层下存放接收前端请求参数的对象 */ private String voModuleName=ModuleUtil.merge(entityModuleName,"vo"); /** * factory层下存放dto,vo,dao三层转换的对象工厂 */ private String factoryModuleName=ModuleUtil.merge(entityModuleName,"factory");; /** * controller模块名 */ private String controllerModuleName="controller"; /** * mapper模块名 */ private String mapperModuleName="mapper"; /** * service模块名 */ private String serviceModuleName="service"; /** * serviceImpl模块名 */ private String serviceImplModuleName=ModuleUtil.merge(serviceModuleName,"impl"); /** * 是否启用默认模板配置: 会自动生成默认提供的模板代码 */ private Boolean defaultConfig=Boolean.TRUE; /** * 模板文件的基础目录: 相对于classPath而言 */ private String templateBaseDir= "template"; public static final String DEFAULT_TEMPLATE_BASE_DIR="default-template"; } ``` SqlTemplateConfig主要用来控制各个模板文件的输出路径,核心公式为: ```java projectPath+basePackageModuleName+对应模块名+文件名 ``` **** ## 模板文件配置 默认模板文件存放地: ![img_5.png](img_5.png) 如果我们不想生成某个默认模板文件,可以动态移除: ```java sqlManager.removeDefaultTemplate(DefaultTemplateManager.CONTROLLER_TEMPLATE); ``` 如果需要自定义模板文件也很简单,只需要三步: 1. 编写模板文件,因为底层使用Velocity模板引擎,大家需要先去简单了解一下它的语法,即可开始使用,模板文件的编写可以参考默认的模板文件 2. 编写对应的RenderHandler,往模板上下文中填充数据,RenderHandler返回的相对路径为: basePackageModuleName+对应模块名+文件名 ```java public class ControllerRenderHandler implements RenderHandler { @Override public String paramPrepare(VelocityContext velocityContext, TableMapInfo tableMapInfo, SqlTemplateConfig sqlTemplateConfig, TemplateContext templateContext) { velocityContext.put("packagePath", sqlTemplateConfig.getControllerModuleName()); velocityContext.put("className",tableMapInfo.getTableName()); velocityContext.put("classNameDto",tableMapInfo.getTableName()+"Dto"); velocityContext.put("serviceModule",sqlTemplateConfig.getServiceModuleName()); velocityContext.put("voModule",sqlTemplateConfig.getVoModuleName()); velocityContext.put("dtoModule",sqlTemplateConfig.getDtoModuleName()); velocityContext.put("cacheFeature", SpringUtil.getBean(GlobalProperties.class).getFeature().getCache()); templateContext.setVal(TemplateContext.JAVA_FILE_NAME,tableMapInfo.getTableName()+"Controller.java"); return sqlTemplateConfig.getControllerModuleName(); } } ``` 3.使用SqlManager添加自定义模板 ```java sqlManager.addTemplate("hello.vm",new HelloRenderHandler()); ``` 4.默认会去类路径下的template目录下寻找我们的自定义模板文件,可以在yml配置进行修改 ```java /** * 模板文件的基础目录: 相对于classPath而言 */ private String templateBaseDir= "template"; ``` 5.我们的模板文件最终生成路径为: projectPath+RenderHandler返回的相对路径 **** # 模拟数据生成 如果我们要使用模拟数据生成的功能,我们需要配置该功能为开启状态: ```java easy-generator: global: mock-data: true ``` 如何生成模拟数据,我们只使用使用两个注解提示生成器即可: - @TableMock: ```java /** * 标注在类上,指明表名,要插入的数据条数 */ @Retention(RetentionPolicy.RUNTIME) public @interface TableMock { /** * @return 表名,不指定默认为类名首字母小写并进行驼峰转换 */ String tableName() default ""; /** * @return 模拟生成的数据条数 */ int mockNum() default 10; } ``` - @FieldMock ```java /** * 标注在字段上,指明模拟数据生成的规则: * 1.固定 * 2.随机 * 3.递增 * 4.规则 * 5.词库 */ @Retention(RetentionPolicy.RUNTIME) public @interface FieldMock { /** * @return 列名,默认为字段名小写并且进行驼峰到下划线的转换 */ String fieldName() default ""; /** * @return 模拟数据生成的规则 */ int rule(); /** * 指定额外信息: * 1. 固定值,指明固定值的大小 * 2. 随机值,指明随机类型 * 3. 递增值,指明递增初值 * 4. 规则值,指明正则表达式 * 5. 词库值,指明来源于哪个词库,除非使用默认词库,否则需要指明词库文件的全路径 */ String extra(); } ``` 随机值的额外信息比较丰富: ```java /** * 随机规则下的随机生成类型值选择 */ public interface RandomRuleConstants { /** * 随机生成一个字符串 */ String STRING="STRING"; /** * 随机生成一个整数 */ String INT="INT"; /** * 随机生成一个小数 */ String FLOAT="FLOAT"; /** * 随机生成一个日期 */ String DATE="DATE"; /** * 随机生成一个时间戳 */ String TIMESTAMP="TIMESTAMP"; /** * 随机生成一个网址 */ String URL="URL"; /** * 随机生成一个IP */ String IP="IP"; /** * 随机生成一个邮箱 */ String EMAIL="EAMIL"; /** * 随机生成一个姓名 */ String NAME="NAME"; /** * 随机生成一个城市名 */ String CITY="CITY"; /** * 随机生成一个大学名 */ String UNIVERSITY="UNIVERSITY"; /** * 随机生成一个电话号码 */ String PHONE="PHONE"; /** * 随机生成一个年龄 */ String AGE="AGE"; } ``` 使用演示: ```java @Table(comment = "学生表") @TableMock(mockNum = 20) public class Stu { @Key(keyType = KeyTypeConstants.PRIMARY_KEY) @Filed(comment = "主键id", autoIncrement = true) private Integer id; @Filed(comment = "姓名") @FieldMock(rule = MockRuleConstants.RANDOM, extra = RandomRuleConstants.NAME) private String name; @Filed(comment = "年龄") @FieldMock(rule = MockRuleConstants.RANDOM, extra = RandomRuleConstants.AGE) private Integer age; @Filed(comment = "就读大学") @FieldMock(rule = MockRuleConstants.DICT, extra = DefaultDictConstants.UNIVERSITY_DICT) private String university; @Filed(comment = "主攻编程语言") @FieldMock(rule = MockRuleConstants.DICT,extra = DefaultDictConstants.LANGUAGE_DICT) private String language; @Filed(comment = "老家城市") @FieldMock(rule = MockRuleConstants.DICT, extra = DefaultDictConstants.CITY_DICT) private String homeCity; @Filed(comment = "电话号码") @FieldMock(rule = MockRuleConstants.RANDOM, extra = RandomRuleConstants.PHONE) private String phoneNumber; @Filed(comment = "qq邮箱号码") @FieldMock(rule = MockRuleConstants.RULE, extra = "[1-9][0-9]{8,10}\\@[q][q]\\.[c][o][m]") private String qqEamil; @Filed(comment = "出生日期") @FieldMock(rule = MockRuleConstants.RANDOM, extra = RandomRuleConstants.DATE) private LocalDateTime birthTime; @Filed(comment = "创建时间", defaultVal = DefaultValConstants.CURRENT_TIMESTAMP) private LocalDateTime createTime; @Filed(comment = "修改时间", defaultVal = DefaultValConstants.CURRENT_TIMESTAMP) private LocalDateTime updateTime; } ``` 测试: ```java @SpringBootTest public class StuTableCreateAndMockTest { @Autowired private SqlManager sqlManager; @Test public void test(){ sqlManager.addMockObject(new Stu()); //如果我们不需要根据OopBuilder生成模板文件和执行建库sql,可以注释掉,前提是确保相关表已经存在 //sqlManager.addMappedObject(new Stu()); sqlManager.build(); } } ``` ![img_6.png](img_6.png) 对于没有标注@FieldMock注解的字段,那么再生成对应的Insert语句时,会跳过该字段的插入: ```java insert into tableName(mockFiled1,mockFiled2,...) values(mockVal1,mockVal2,...); ``` **** ## 自定义词库 如果需要引入自定义词库,需要: ```java @FieldMock(rule = MockRuleConstants.DICT,extra = "词库文件的路径") private String language; ``` 词库文件中词语的格式如下: ![img_7.png](img_7.png) 多个词语间通过英文的逗号进行分隔。 **** # 索引注意事项 通过在字段上标注@Key注解来指定索引: ```java @Key(keyType = KeyTypeConstants.PRIMARY_KEY) ``` 对于通用的索引,我们可以直接通过KeyTypeConstants完成枚举: ```java public interface KeyTypeConstants { String PRIMARY_KEY="PRIMARY KEY"; String UNIQUE_KEY="UNIQUE"; String KEY="KEY"; String FOREIGN_KEY="FOREIGN KEY"; } ``` 对于特定数据库支持的索引,例如mysql,可以根据MysqlKeyTypeConstants完成枚举: ```java public interface MysqlKeyTypeConstants { String PRIMARY_KEY="PRIMARY KEY"; String COMPOSE_PRIMARY_KEY= "PRIMARY KEY"; String UNIQUE_KEY="UNIQUE"; String COMPOSE_UNIQUE_KEY_="UNIQUE"; String KEY="KEY"; String COMPOSE_KEY="KEY"; String FOREIGN_KEY="FOREIGN KEY"; } ``` **** # 从已经存在的表完成映射,生成模板代码 如果表已经不存在了,不需要重新创建,但是此时我们需要根据已经存在的表生成对应的模板文件,那么可以进行如下配置: ```java easy-generator: template: project-path: C:\Users\zdh\IdeaProjects\Easy-generator\src\main\java base-package-module-name: com.easyCode.test global: #是否通过sqlExec执行建库语句 exec-sql: true #记录建库语句和insert插入语句到文件中保存 print-sql-to-file: true #是否开启sqlMapper生成模板文件的功能 sqlMapper: true #是否需要去解析Mock相关注解完成模拟数据的生成 mock-data: true #从已经存在的表中导入数据 import-from-exist-table: true #要映射的表名,如果存在多个表需要映射,用逗号分隔: stu,course exist-table-name: stu ``` 如果import-from-exist-table属性被设置为true,那么即使exec-sql为true,也会跳过sqlExec组件的执行,因为表已经存在了,也不会生成相关的建库语句来执行。 **** # 使用步骤 1. 拉取项目到本地,通过maven install安装到本地仓库 ![在这里插入图片描述](https://img-blog.csdnimg.cn/4dbd697876b542c18c29d29b58007399.png) 2. 在个人项目中引入坐标 ```xml easyCode.dhy Easy-generator 1.0-SNAPSHOT ``` 因为采用SpringBoot自动配置,所以个人项目只需要引入Spring相关依赖后,创建一个启动类,然后再正常写测试类,完成代码生成即可,包括模拟数据,当然还需要修改一下模板代码的输出路径。