- 适用于 Spring Boot 架构
- 基于数据表结构定义,自动生成 CRUD 代码,省时省力
- 自动检测数据表字段类型、字段长度、数值精度、主键字段、唯一索引字段
- 支持 Oracle、MySQL(Percona/MariaDB)、Microsoft SQLServer、PostgreSQL 等四种类型数据库
- 支持生成原版 Mybatis 以及 Mybatis通用Mapper 、MybatisPlus相关代码
- 支持生成 Mybatis 分页代码(基于 Mybatis-PageHelper)
- 支持生成基于 Hibernate Validator 的参数校验注解
- 支持生成基于 Spring Cloud OpenFeign 的服务消费端代码
- 支持生成基于 Swagger 的API接口及参数注解
- 支持生成 Postman 可导入的API接口JSON定义文件
- 支持生成 JUnit 所支持的单元测试用例文件
- 支持生成基于 CURL 的命令行测试用例脚本文件
模板名 | 含义 | 路径 | 备注 | 表字段变化后是否重新生成 |
---|---|---|---|---|
vo.ftl | VO类 | java/vo/ | 用于接收/输出数据 | 是 |
queryVo.ftl | 查询条件 | java/vo/ | 用于接收查询条件 | 是 |
dto.ftl | DTO类 | java/dto/ | 适用于service层 | 是 |
queryDto.ftl | 查询条件 | java/dto/ | 适用于service层 | 是 |
controller.ftl | 控制器代码 | java/controller/ | 对外暴露HTTP接口 | 该文件只需生成1次 |
serviceInterface.ftl | 服务接口定义 | java/service/ | 该文件只需生成1次 | |
origDomain.ftl | 实体类 | java/entity/ | 适用于原版mybatis | 是 |
origMapperClass.ftl | Mapper接口 | java/dao/ | 适用于原版mybatis | 该文件只需生成1次 |
origMapperXml.ftl | Mapper XML | resources/ | 适用于原版mybatis | 是 |
origServiceImpl.ftl | 服务接口实现 | java/service/ | 适用于原版mybatis | 是 |
tkDomain.ftl | Domain实体类定义 | java/domain/ | 适用于mybatis通用Mapper | 是 |
tkMapperClass.ftl | Mapper接口 | java/dao/ | 适用于mybatis通用Mapper | 该文件只需生成1次 |
tkMapperXml.ftl | Mapper XML | resources/ | 适用于mybatis通用Mapper | 该文件只需生成1次 |
tkServiceImpl.ftl | 服务接口实现 | java/service/ | 适用于mybatis通用Mapper | 是 |
mpDomain.ftl | Domain实体类定义 | java/domain/ | 适用于MybatisPlus | 是 |
mpMapperClass.ftl | Mapper接口 | java/dao/ | 适用于MybatisPlus | 该文件只需生成1次 |
mpMapperXml.ftl | Mapper XML | resources/ | 适用于MybatisPlus | 该文件只需生成1次 |
mpServiceImpl.ftl | 服务接口实现 | java/service/ | 适用于MybatisPlus | 是 |
feignClient.ftl | FeignClient服务接口 | java/feign/ | 适用于Spring Cloud消费者端 | 该文件只需生成1次 |
feignClientFallback.ftl | FeignClient服务接口Fallback实现 | java/feign/ | 适用于Spring Cloud消费者端 | 该文件只需生成1次 |
commonConverter.ftl | 通用对象转换工具 | java/util/ | 用于VO/DTO/DO等对象之间的转换 | 该文件只需生成1次 |
converter.ftl | 对象转换工具类 | java/util/ | 用于VO/DTO/DO等对象之间的转换 | 是 |
validatorInsertGroup.ftl | 校验分组(插入) | java/validator/ | 用于Hibernate Validator分组校验 | 该文件只需生成1次 |
validatorUpdateGroup.ftl | 校验分组(更新) | java/validator/ | 用于Hibernate Validator分组校验 | 该文件只需生成1次 |
unitTestCase.ftl | JUnit单元测试用例 | java/test/ | 用于JUnit单元测试 | 是 |
postmanCollection.ftl | Postman接口定义 | json/ | 使用方法:Postman>Import | 是 |
postmanEnvironment.ftl | Postman环境变量定义 | json/ | 使用方法:Postman>Manage Environment>Import | 该文件只需生成1次 |
curl-add.ftl | CURL新增记录命令及参数 | other/ | 使用方法:直接命令行调用 | 是 |
curl-update.ftl | CURL更新记录命令及参数 | other/ | 使用方法:直接命令行调用 | 是 |
curl-delete.ftl | CURL删除记录命令及参数 | other/ | 使用方法:直接命令行调用 | 是 |
html-form.ftl | 标准HTML表单 | other/ | 包含各个字段的标准HTML表单 | 是 |
- 数据表有主键字段时,程序将直接使用该字段
- 数据表无主键字段时,程序将使用最后一个具有唯一索引的字段
- 数据表既无主键字段也无唯一索引字段时,程序将使用 TableContext 对象中 primaryKeyColumn 参数所指定的字段
public class App {
public static void main(String[] args) throws Exception {
JdbcInfo param = new JdbcInfo();
//指定数据库类型
param.setDbType(DatabaseType.ORACLE);
//数据库主机名或IP
param.setHost("192.168.2.102");
//数据库端口号
param.setPort("1521");
//schema名称(oracle和PostgreSQL填写Schema名称,mysql或sqlserver则填写数据库名称)
param.setSchema("sys_biz");
//数据库用户名
param.setUsername("biz_manager");
//数据库用户密码
param.setPassword("123456");
//数据库实例名(oracle填写实例名,PostgreSQL填写数据库名称,mysql或sqlserver留空)
param.setServiceName("newbizdb");
TableCodeGenerator generator = new TableCodeGenerator(param);
RunParam rp = new RunParam();
rp.setOutputPath("E:\\tmp\\generated");
//表名
TableContext table = TableContext.withName("T_ORDER_INFO");
//需去掉的表名前缀
table.setTableNamePrefixToRemove("T_");
rp.addTable(table);
//结果使用Result类来包装,如 MyResult<Boolean>
rp.setResultClass("com.test.common.MyResult");
//使用Mybatis通用Mapper作为dao层中间件
generator.setDaoType(DaoType.TkMyBatis);
//将表名从下划线转驼峰后再加上Entity后缀作为类名
generator.setClassNameGenerator(t -> org.apache.commons.text.CaseUtils.toCamelCase(t, true, '_') + "Entity");
//生成
generator.run(rp);
}
}
RunParam 的可选参数设置:
方法名 | 含义 | 备注 |
---|---|---|
setAuthor() | 指定生成的javadoc注释中author的名称 | 留空则默认使用当前操作系统用户名 |
setBasePkgName() | 指定java基础包名 | 留空则默认使用com.example.myapp |
setOutputPath() | 指定输出目录的绝对路径 | 留空则默认输出到当前用户主目录 |
setBaseEntityClass() | 如果VO/DO/DTO等实体类需要继承某个基础类,可以在此指定基础类的完整路径 | 留空则默认无 |
setResultClass() | 如果service层及接口层返回值需要使用某个基础类进行包装,可以在此指定该类的完整路径 | 留空则默认无 |
TableContext 的可选参数设置:
方法名 | 含义 | 备注 |
---|---|---|
setTableNamePrefixToRemove() | 指定需去掉的表名前缀 | 留空则不去掉任何前缀 |
setLikeColumns() | 指定启用模糊查询的字段名(用于字符类型字段,多个以逗号隔开) | 如果字段为字符类型则在相应QueryDTO类中增加 xxxLike 属性 |
setRangeColumns() | 指定启用取值范围查询的字段名(用于时间或数字类型字段,多个以逗号隔开) | 如果字段为时间或数字类型则在相应QueryDTO类中增加 xxxMin/xxxMax 属性 |
setInColumns() | 指定启用 IN 查询的字段名(多个以逗号隔开) | 在相应QueryDTO类中增加 xxxIn 属性 |
setNotInColumns() | 指定启用 NOT IN 查询的字段名(多个以逗号隔开) | 在相应QueryDTO类中增加 xxxNotIn 属性 |
setPrimaryKeyColumn() | 手动指定主键字段名(不区分大小写) | 如果程序无法自动检测到主键字段,则在此参数指定(适用于无主键且无唯一索引的表) |
setVersionColumn() | 如果该表有乐观锁,可在此指定其字段名(不区分大小写) | 留空则默认无 |
setPageSize() | 指定默认分页大小(一个大于0的整数) | 留空则默认为10 |
setSequenceName() | 针对Oracle数据库,可以指定序列名称 | 留空则默认使用 SEQ_表名 作为序列名称 |
setLogicDeleteColumn() | 如果该表需要实现逻辑删除功能,指定相应字段名,所有删除操作不再是物理删除而是逻辑删除 | 留空则无逻辑删除功能 |
TableCodeGenerator 的可选参数设置:
方法名 | 含义 | 备注 |
---|---|---|
setGlobalTableNamePrefixToRemove() | 需去掉的表名前缀(全局) | 如果需要去掉的表名前缀均相同,则可以全局配置它,不再需要在TableContext中逐个配置前缀; 如果二者同时有值,则使用TableContext中的值 |
setUseDubboService() | 是否使用 Dubbo 的@Service注解 | 留空则默认使用 Spring 的@Service注解 |
setUseSwagger() | 是否生成swagger相关注解 | 留空则默认true |
setDaoType() | 指定dao层中间件的类型(三选一:原版MyBatis/Mybatis通用Mapper/MyBatisPlus) | 留空则默认使用原版MyBatis |
setClassNameGenerator() | 指定类名的自定义生成规则 | 留空则默认将表名从下划线形式转成驼峰形式作为类名 |
- 如果您采用原版 Mybatis,不应在 resources/XXXMapper.xml 中编写自己的业务逻辑(因为一旦重新生成代码,该文件也会一起重新生成,会丢失您自行编写的业务代码);建议自行继承 XXXMapper,然后在新的xml映射文件中编写自己的业务逻辑
- 如果您采用 Mybatis通用Mapper,可以在 resources/XXXCommonMapper.xml 中编写自己的业务逻辑(该文件不会重新生成)
- 如果数据表字段变化比较频繁,建议采用 Mybatis通用Mapper 或 MybatisPlus
- 个人意见:MybatisPlus 本身是一个优秀的增强插件,但它的开发团队想做的事情太多,且其中某些开发人员缺乏对开源项目负责任的职业态度,导致其API相对较乱(如版本平滑升级等),相比之下,如果希望项目规模扩大后依然规范,且需要长期维护,那么建议选用目前的 Mybatis通用Mapper!
- 如果使用了Mybatis通用Mapper或MybatisPlus,则您的java版本需要1.8或更高(因为代码中使用了Lambda表达式)
数据库中的数据类型 | 实体类中Java变量类型 | 备注 |
---|---|---|
bigint | Long | |
binary | Byte[] | |
bit | Boolean | |
blob | Byte[] | |
char | String | |
date | Date | |
datetime | Date | |
decimal | BigDecimal | |
double | Double | |
float | Float | |
image | Byte[] | |
int | Integer | |
int4 | Integer | |
int8 | Long | |
longblob | Byte[] | |
mediumblob | Byte[] | |
money | BigDecimal | |
nchar | String | |
ntext | String | |
number | Long | 无小数时 |
number | BigDecimal | 有小数时 |
numeric | BigDecimal | |
nvarchar | String | |
nvarchar2 | String | |
real | Float | |
smalldatetime | Date | |
smallint | Integer | |
smallmoney | BigDecimal | |
sql_variant | String | |
text | String | |
timestamp | Date | |
timestamp(6) | Date | |
tinyint | Integer | |
uniqueidentifier | String | |
varbinary | Byte[] | |
varchar | String | |
varchar2 | String |
- 数据表应当包含1个唯一索引或主键字段(可以与具体业务无关)
- 如果指定Oracle数据库,则表名、字段名、序列名会统一为大写形式
- 如果指定MySQL数据库,则表名、字段名会统一为小写形式,且会自动加上反引号
- 编写自定义的SQL语句(用于查询数据库中的表名、表注释、字段名、字段注释、字段类型、字段长度、主键、唯一索引等),约定保存路径为 resources/sql_XXX.xml
- 编写您自定义的DbUtil类继承 AbstractDbUtil 抽象类,如数据库有特殊逻辑,也可在该类中编写
- 在 dbutils-config.json 文件中配置您自定义的DbUtil类映射及驱动名称等信息
- 不要忘记在 pom.xml 中加入相应的 jdbc 驱动
代码模板基于 Freemarker模板引擎 编写,因此您可以遵循该模板的语法自行实现新的代码模板。提供的上下文变量包括:
变量 | 含义 | 变量类型 |
---|---|---|
${basePkgName} | 基础包名 | String |
${baseEntityClass} | 基础实体类的完整路径 | String |
${timeZone} | 时区名称 | String |
${useDubboServiceAnnotation} | 是否启用Dubbo服务(0否/1是) | int |
${useSwagger} | 是否启用SwaggerUI(0否/1是) | int |
${table} | 数据表根对象 | Object |
${table.name} | 数据表原始名称 | String |
${table.kebabCaseName} | kebabCase形式的表名 | String |
${table.dbType} | 数据库类型(oracle/mysql/sqlserver/postgresql,小写形式) | String |
${table.subPkgName} | 子包名 | String |
${table.javaClassName} | Java类名称(首字母大写) | String |
${table.javaClassNameLower} | Java类名称(首字母小写) | String |
${table.comments} | 数据表注释 | String |
${table.imports} | Java类中需要import的类名集合 | Set |
${table.author} | 生成的javadoc注释中author的名称 | String |
${table.pageSize} | 默认分页大小 | int |
${table.versionColumn} | 乐观锁版本号字段名 | String |
${table.columns} | 数据表中的所有字段信息列表 | List |
字段信息包括:
变量 | 含义 | 变量类型 |
---|---|---|
${column.columnName} | 原始列名 | String |
${column.columnCamelNameLower} | 驼峰形式列名(首字母小写) | String |
${column.columnCamelNameUpper} | 驼峰形式列名(首字母大写) | String |
${column.columnComment} | 列注释 | String |
${column.columnType} | 列类型(数据库中的类型) | String |
${column.columnJavaType} | 列类型对应的Java类型 | String |
${column.columnMyBatisType} | 列类型对应的mybatis jdbcType | String |
${column.columnLength} | 列长度 | int |
${column.columnPrecision} | 列精度(针对数字列) | int |
${column.columnScale} | 列小数位数(针对数字列) | int |
${column.nullable} | 是否可空(0否/1是) | int |
${column.charLength} | 列长度(针对字符列) | int |
${column.defaultValue} | 列默认值 | String |
${column.isNumber} | 是否为数字列(0否/1是) | int |
${column.isChar} | 是否为字符列(0否/1是) | int |
${column.isPrimaryKey} | 是否为主键(0否/1是) | int |
${column.enableLike} | 是否启用 LIKE 模糊查询(0否/1是,仅限于字符列) | int |
${column.enableIn} | 是否启用 IN 查询(0否/1是) | int |
${column.enableRange} | 是否启用范围(大于等于、小于等于)查询(0否/1是,仅限于数字和日期时间类型的列) | int |
${column.enableNotIn} | 是否启用 NOT IN 查询(0否/1是) | int |
编写完模板文件之后,在 template-config.json 文件中配置该模板的相关信息即可。
- 针对数据插入操作,根据 InsertGroup 分组进行校验;
- 针对数据更新操作,根据 UpdateGroup 分组进行校验;
- 其它的共有校验规则(如字段长度限制等),根据 Default 分组进行校验;
通过 @ExceptionHandler 捕获 MethodArgumentNotValidException 和 BindException 异常即可。
区别:
- 如果使用表单对象(Form)形式接收参数(如查询操作),则出现 BindException 异常
- 如果使用 @RequestBody 形式接收参数(如插入操作),则出现 MethodArgumentNotValidException 异常
参考代码:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
@ResponseBody
public String errorHandler(Exception ex) {
if (ex instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException me = (MethodArgumentNotValidException) ex;
return getFieldErrors(me.getBindingResult().getFieldErrors());
} else if (ex instanceof BindException) {
BindException be = (BindException) ex;
return getFieldErrors(be.getBindingResult().getFieldErrors());
} else {
return ex.getMessage();
}
}
private String getFieldErrors(List<FieldError> fieldErrors) {
String msg = "error";
if (!fieldErrors.isEmpty()) {
List<String> errorMsgs = fieldErrors.stream().map(FieldError::getDefaultMessage).distinct().collect(Collectors.toList());
msg = String.join(";", errorMsgs);
}
return msg;
}
}
默认情况下 Hibernate Validator 使用普通模式:校验器会校验完所有的属性,然后返回所有的验证错误信息。
如果希望使用 Fail-fast(快速失败) 模式,则需要增加额外配置:
@Configuration
public class HibernateValidatorConfig {
public HibernateValidatorConfig() {
}
@Bean
public Validator myValidatorFactory() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory();
return validatorFactory.getValidator();
}
}
使用该模式之后,当校验器遇到第1个不满足条件的参数时就立即结束校验工作,只返回这一个参数对应的错误信息。
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
自动生成的代码中用到了一些第三方开源组件,它们的maven坐标如下(版本号请自行匹配):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>X.X.X</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>X.X.X</version>
</dependency>
实际单元测试中用到的数据库版本:
- Oracle 11g
- MySQL 5.5/5.6/5.7/5.8
- MariaDB 10.2.x/10.3.x/10.4.x
- Microsoft SQL Server 2008 R2
- PostgreSQL 12.3
- Percona Server(未实际测试,理论上也兼容)