最近接受到一个任务,代码生成器的开发。当时自己有两种思路来实现,第一种:自己写个程序,通过java代码生成,第二种:寻找第三方插件来支持,引用他们的。当时自己开发了一个代码生成器程序,但是不够完善,不能全面满足需求,还是借鉴他人意见,网上查询资料,发现Mybatis-plus插件还是挺不错,分享给大家,不过本人才疏学浅,若有说的不对,还望大家指出来。
- Mybatis-plus官网,有兴趣的可以了解了解,学习学习的。
- 官网配置代码生成器的例子。
当时针对参数校验方面,是需要判断表中字段非空约束,可是查看了Mybatis-plus的源代码,一直没有找出非空字段,幸好TableField中有个 customMap map结构,只好通过jdbc去查询sql,获取字段的非空约束,再设置到customMap中。
package com.poi.code.creator.tool;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.config.rules.PropertyInfo;
import com.google.common.collect.Maps;
import com.poi.code.creator.config.OrdinaryGeneratorConfig;
import com.poi.code.creator.util.SQLExecutor;
//import lombok.Data;
//import lombok.extern.slf4j.Slf4j;
/**
* <p>
* 方式二:代码生成器
* 可按照表 生成crud。
* </P>
*
* @author : dengbin
* @date : 2019/10/11
*/
//@RunWith(SpringRunner.class)
//@SpringBootTest(classes = Application.class)
//@Data
//@Slf4j
public class OrdinaryCodeGenerator {
// @Autowired
// private OrdinaryGeneratorConfig ordinaryGeneratorConfig;
private final static String sql = "SELECT a.attnum,\n" +
" a.attname AS name,\n" +
" t.typname AS type,\n" +
" a.attlen AS length,\n" +
" a.atttypmod AS lengthvar,\n" +
" a.attnotnull AS isNull,\n" +
" b.description AS columnComment\n" +
" FROM pg_class c,\n" +
" pg_attribute a\n" +
" LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,\n" +
" pg_type t\n" +
" WHERE c.relname =" + " ? \n" +
" and a.attnum > 0\n" +
" and a.attrelid = c.oid\n" +
" and a.atttypid = t.oid\n" +
" ORDER BY a.attnum";
private final static Map<String, Object> params = Maps.newHashMap();
private final static List<String> placeholderNameList = Collections.singletonList("tableName");
public static void main(String[] args) throws IOException {
OrdinaryGeneratorConfig ordinaryGeneratorConfig = new OrdinaryGeneratorConfig();
ordinaryGeneratorConfig.setAbsolutePath("src/main/java");
ordinaryGeneratorConfig.setAuthor("dengbin");
ordinaryGeneratorConfig.setRelativeName("com.poi.code.creator");
ordinaryGeneratorConfig.setTables("ent_i_meter");
generateCode(ordinaryGeneratorConfig);
}
public static void generateCode(OrdinaryGeneratorConfig ordinaryGeneratorConfig) throws IOException {
String author = ordinaryGeneratorConfig.getAuthor();
String resultPath = ordinaryGeneratorConfig.getAbsolutePath() + "/" + ordinaryGeneratorConfig.getRelativeName().replace(".", "/");
boolean exists = new File(resultPath).exists();
if (!exists) {
throw new RuntimeException("目录:" + resultPath + "不存在,请重新确认!");
}
String tables = ordinaryGeneratorConfig.getTables();
params.put("tableName", tables);
//获取数据库连接
GlobalConfig config = new GlobalConfig();
String dbUrl = "你的数据库url链接";
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.POSTGRE_SQL)
.setUrl(dbUrl)
.setUsername("你的数据库用户名称")
.setPassword("你的数据库用户账号")
.setDriverName("org.postgresql.Driver")
.setSchemaName("**");
//类型转换
dataSourceConfig.setTypeConvert(new PostgreSqlTypeConvert() {
@Override
public PropertyInfo processTypeConvert(GlobalConfig globalConfig, String fieldType) {
System.out.println("转换类型:" + fieldType);
// 注意!!processTypeConvert 存在默认类型转换,如果不是你要的效果请自定义返回、非如下直接返回。
if (fieldType.contains("numeric")) {
return DbColumnType.DOUBLE;
} else {
return super.processTypeConvert(globalConfig, fieldType);
}
}
});
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig
.setCapitalMode(true)
.setLogicDeleteFieldName("rec_status")
.setEntityLombokModel(false)
.setNaming(NamingStrategy.underline_to_camel)
// .setTablePrefix("eff_","ent_i_","ent_s_","ent_r_","eqp_i_","eqp_r_","eqp_s_","prod_i_","prod_r_","prod_s_","sys_i_")
.setInclude(tables) //修改替换成你需要的表名,多个表名传数组
.setEntityLombokModel(true); //是否使用lombok
config.setActiveRecord(true)
.setAuthor(author)
.setOutputDir(ordinaryGeneratorConfig.getAbsolutePath())
.setEnableCache(false)
.setBaseColumnList(false)
.setBaseColumnList(false)
.setIdType(IdType.AUTO) //主键类型 定死id自增
.setFileOverride(true)
.setServiceName("%sService")
.setSwagger2(true); //是否使用Swagger
InjectionConfig ic = new InjectionConfig() {
@Override
public void initMap() {
this.setMap(getMap());
}
};
TemplateConfig tc = new TemplateConfig();
tc.setXml(null)
.setServiceImpl(null)
.setController("template/controller.java.vm")
.setEntity("template/entity.java.vm")
.setService("template/service.java.vm");
//添加add 请求对象
List<FileOutConfig> foc = new ArrayList<>();
foc.add(new FileOutConfig("/template/addVo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
setCustomMap(tableInfo);
return resultPath + "/vo/req/Add" + tableInfo.getEntityName() + "Req"
+ StringPool.DOT_JAVA;
}
});
//添加update 请求对象
foc.add(new FileOutConfig("/template/updateVo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
setCustomMap(tableInfo);
return resultPath + "/vo/req/Update" + tableInfo.getEntityName() + "Req"
+ StringPool.DOT_JAVA;
}
});
//添加list请求对象
foc.add(new FileOutConfig("/template/listVo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return resultPath + "/vo/req/List" + tableInfo.getEntityName() + "Req"
+ StringPool.DOT_JAVA;
}
});
//添加分页请求对象
foc.add(new FileOutConfig("/template/pageVo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return resultPath + "/vo/req/Page" + tableInfo.getEntityName() + "Req"
+ StringPool.DOT_JAVA;
}
});
ic.setFileOutConfigList(foc);
new AutoGenerator()
.setGlobalConfig(config)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setTemplate(tc)
.setCfg(ic)
.setPackageInfo(
new PackageConfig()
.setParent(ordinaryGeneratorConfig.getRelativeName())
.setController("controller")
.setEntity("entity")
).execute();
}
private static void setCustomMap(TableInfo tableInfo) {
List<Map<String, Object>> execute = SQLExecutor.execute(sql, params, placeholderNameList);
List<TableField> fields = tableInfo.getFields();
for (TableField field : fields) {
Optional<Map<String, Object>> mapOptional = execute.stream().filter(item -> item.get("name").equals(field.getName())).findAny();
Map<String, Object> customMap = Maps.newHashMap();
mapOptional.ifPresent(stringObjectMap -> customMap.put("isNull", (Boolean) stringObjectMap.get("isnull")));
field.setCustomMap(customMap);
}
}
}
controller的模板(controller.java.vm)内容如下:
package ${package.Controller};
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ${package.Service.replace(".service","")}.service.${table.serviceName} ;
import ${package.Service.replace(".service","")}.vo.req.Add${entity}Req;
import ${package.Service.replace(".service","")}.vo.req.List${entity}Req;
import ${package.Service.replace(".service","")}.vo.req.Page${entity}Req;
import ${package.Service.replace(".service","")}.vo.req.Update${entity}Req;
import com.poit.commons.utils.resp.ApiResp;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
/**
* @author ${author}
* @since ${date}
*/
@Api(description = "$!{table.comment}")
@RestController
@RequestMapping("/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end" )
@Slf4j
public class ${table.controllerName} {
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
@ApiOperation("列表")
@GetMapping("/list")
public ApiResp list(List${entity}Req req) throws Exception {
return ${table.entityPath}Service.list(req);
}
@ApiOperation("分页")
@GetMapping("/page")
public ApiResp page(Page${entity}Req req) throws Exception {
return ${table.entityPath}Service.page(req);
}
@ApiOperation("获取详情")
@GetMapping("/get")
public ApiResp get(#foreach($field in ${table.fields})#if(${field.keyFlag})@RequestParam("${field.propertyName}") ${field.propertyType} ${field.propertyName}#end#end) throws Exception {
return ${table.entityPath}Service.get(#foreach($field in ${table.fields})#if(${field.keyFlag})${field.propertyName}#end#end);
}
@ApiOperation("添加")
@PostMapping("/add")
public ApiResp add(@Valid @RequestBody Add${entity}Req req) throws Exception {
return ${table.entityPath}Service.add(req);
}
@ApiOperation("修改")
@PostMapping("/modify")
public ApiResp modify(@Valid @RequestBody Update${entity}Req req) throws Exception {
return ${table.entityPath}Service.modify(req);
}
@ApiOperation("删除")
@GetMapping("/delete")
public ApiResp delete(#foreach($field in ${table.fields})#if(${field.keyFlag})@RequestParam("${field.propertyName}") ${field.propertyType} ${field.propertyName}#end#end) throws Exception {
return ${table.entityPath}Service.delete(#foreach($field in ${table.fields})#if(${field.keyFlag})${field.propertyName}#end#end);
}
}
entity.java.vm
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
#end
import com.baomidou.mybatisplus.annotation.TableName;
import javax.validation.constraints.Size;
/**
* <p>
* ${entity}对象
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@Data
#end
@TableName("${table.name}")
#if(${swagger2})
@ApiModel(value = "${entity}对象", description = "$!{table.comment}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable{
#end
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
#if(${swagger2})
@ApiModelProperty(value = "${field.comment}")
#end
#end
#if(${field.keyFlag})
#if(${field.keyIdentityFlag})
@TableId(value = "${field.name}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.name}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.name}")
#end
#elseif(${field.fill})
#if(${field.convert})
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.name}")
#end
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
#if(${field.columnType}=="STRING" && ${field.type} !="text")
@Size(max = ${field.type.replace("character varying(","").replace(")","")}, message = "${field.comment}应该在0-${field.type.replace("character varying(","").replace(")","")}字符之间")
#end
private ${field.propertyType} ${field.propertyName};
#end
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}(){
return ${field.propertyName};
}
#if(${entityBuilderModel})
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}){
#else
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${entityBuilderModel})
return this;
#end
}
#end
#end
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} ="${field.name}";
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal(){
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}
service.java.vm
package ${package.Service};
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service.replace(".service","")}.vo.req.Add${entity}Req;
import ${package.Service.replace(".service","")}.vo.req.List${entity}Req;
import ${package.Service.replace(".service","")}.vo.req.Page${entity}Req;
import ${package.Service.replace(".service","")}.vo.req.Update${entity}Req;
import com.poit.commons.utils.resp.ApiResp;
/**
* <p>
* ${entity}服务类
* </p>
*
* @author ${author}
* @since ${date}
*/
@Service
public class ${table.serviceName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> {
public ApiResp list(List${entity}Req req)throws Exception{
String keyword = req.getKeyword();
QueryWrapper<${entity}> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(keyword)) {
queryWrapper.like("", req.getKeyword());
}
return ApiResp.of(baseMapper.selectList(queryWrapper));
}
public ApiResp page(Page${entity}Req req)throws Exception{
String keyword = req.getKeyword();
IPage<${entity}> page = new Page<>(req.getPageNum(), req.getPageSize());
QueryWrapper<${entity}> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(keyword)) {
queryWrapper.like("", keyword);
}
return ApiResp.of(baseMapper.selectPage(page, queryWrapper));
}
public ApiResp add(Add${entity}Req req)throws Exception{
${entity} data = new ${entity}();
BeanUtils.copyProperties(req, data);
#foreach($field in ${table.fields})
#if(${field.propertyName.equals("createTime")})
data.set${field.capitalName}(new Date());
#end
#end
baseMapper.insert(data);
return ApiResp.of(#foreach($field in ${table.fields})#if(${field.keyFlag})data.get${field.capitalName}()#end#end);
}
public ApiResp modify(Update${entity}Req req)throws Exception{
${entity} data = new ${entity}();
BeanUtils.copyProperties(req, data);
#foreach($field in ${table.fields})
#if(${field.propertyName.equals("updateTime")} || ${field.propertyName.equals("modifyTime")})
data.set${field.capitalName}(new Date());
#end
#end
baseMapper.updateById(data);
return ApiResp.of(Boolean.TRUE);
}
public ApiResp delete(#foreach($field in ${table.fields})#if(${field.keyFlag})${field.propertyType} ${field.propertyName}#end#end)throws Exception{
baseMapper.deleteById(#foreach($field in ${table.fields})#if(${field.keyFlag})${field.propertyName}#end#end);
return ApiResp.of(Boolean.TRUE);
}
public ApiResp get(#foreach($field in ${table.fields})#if(${field.keyFlag})${field.propertyType} ${field.propertyName}#end#end)throws Exception{
return ApiResp.of(baseMapper.selectById(#foreach($field in ${table.fields})#if(${field.keyFlag})${field.propertyName}#end#end));
}
}
接下来VO对象,请求对象,返回对象
package ${package.Service.replace(".service","")}.vo.req;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
* Add${entity}Req 请求对象
* </p>
*
* @author ${author}
* @since ${date}
*/
@Data
@ApiModel(value = "${entity}新增请求对象", description = "$!{table.comment}")
public class Add${entity}Req implements Serializable {
#foreach($field in ${table.fields})
#if(${swagger2} && !${field.propertyName.equals("recStatus")} &&!${field.propertyName.equals("updateTime")} &&!${field.propertyName.equals("createTime")} &&!${field.propertyName.equals("modifyTime")}&&!${field.keyFlag})
#if("$!field.comment" != "")
@ApiModelProperty(value = "${field.comment}")
#else
@ApiModelProperty(value = "")
#end
#if(${field.columnType}=="STRING" && ${field.type} !="text")
@Size(max = ${field.type.replace("character varying(","").replace(")","")}, message = "${field.comment}应该在0-${field.type.replace("character varying(","").replace(")","")}字符之间")
#end
#if(${field.customMap.get("isNull")})
@NotNull(message = "${field.comment}不能为空")
#end
private ${field.propertyType} ${field.propertyName};
#end
#end
}