一、简介
各位网友,大家好,我是阿粉!
最近刚入职一个新团队,还没来得及熟悉业务,甲方爸爸就要求项目要在2个月内完成开发并上线!
本想着往后推迟1个月在交付,但是甲方爸爸不同意,只能赶鸭子上架了!
然后根据业务需求,设计出了大概30多张表,如果这30多张表,全靠开发人员手写 crud,开发所需的时间肯定会大大的延长,甚至可能直接会影响交付时间!
于是就想着,能不能通过代码生成器一键搞定全部的 crud,开发团队只需要根据业务需求编写逻辑代码就可以?
本来计划是用mybatis-plus
的,但是生成的代码,根据现有的框架标准,很多代码也需要自己改,有些地方还不如自己手写用的舒服,因此就决定手写一套代码生成器!
很多新手会觉得代码生成器很个高深的东西。其实不然,一点都不高深,当你看完本文的时候,你会完全掌握代码生成器的逻辑,甚至可以根据自己的项目情况,进行深度定制。
废话也不多说了,直接代码撸上!
二、实现思路
下面我就以SpringBoot
项目为例,数据持久化操作采用Mybatis
,数据库采用Mysql
,编写一个自动生成增、删、改、查等基础功能的代码生成器,内容包括controller
、service
、dao
、entity
、dto
、vo
等信息。
实现思路如下:
- 第一步:获取表字段名称、类型、表注释等信息
- 第二步:基于 freemarker 模板引擎,编写相应的模板
- 第三步:根据对应的模板,生成相应的 java 代码
2.1、获取表结构
首先我们创建一张test_db
表,脚本如下:
CREATE TABLE test_db ( id bigint(20) unsigned NOT NULL COMMENT '主键ID', name varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名称', is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除', create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), KEY idx_create_time (create_time) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='测试表';
表创建完成之后,基于test_db
表,我们查询对应的表结果字段名称、类型、备注信息,这些信息收集将用于后续进行代码生成器所使用!
# 获取对应表结构 SELECT column_name, data_type, column_comment FROM information_sc
同时,获取对应表注释,用于生成备注信息!
# 获取对应表注释 SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_sc
2.2、编写模板
- 编写
mapper
模板,涵盖新增、修改、删除、查询等信息
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="${daoPackageName}.${daoName}"> <!--BaseResultMap--> <resultMap id="BaseResultMap" type="${entityPackageName}.${entityName}"> <#list columns as pro> <#if pro.proName == primaryId> <id column="${primaryId}" property="${primaryId}" jdbcType="${pro.fieldType}"/> <#else> <result column="${pro.fieldName}" property="${pro.proName}" jdbcType="${pro.fieldType}"/> </#if> </#list> </resultMap> <!--Base_Column_List--> <sql id="Base_Column_List"> <#list columns as pro> <#if pro_index == 0>${pro.fieldName}<#else>,${pro.fieldName}</#if> </#list> </sql> <!--批量插入--> <insert id="insertList" parameterType="java.util.List"> insert into ${tableName} ( <#list columns as pro> <#if pro_index == 0>${pro.fieldName},<#elseif pro_index == 1>${pro.fieldName}<#else>,${pro.fieldName}</#if> </#list> ) values <foreach collection ="list" item="obj" separator =","> <trim prefix=" (" suffix=")" suffixOverrides=","> <#list columns as pro> ${r"#{obj." + pro.proName + r"}"}, </#list> </trim> </foreach > </insert> <!--按需新增--> <insert id="insertPrimaryKeySelective" parameterType="${entityPackageName}.${entityName}"> insert into ${tableName} <trim prefix="(" suffix=")" suffixOverrides=","> <#list columns as pro> <if test="${pro.proName} != null"> ${pro.fieldName}, </if> </#list> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <#list columns as pro> <if test="${pro.proName} != null"> ${r"#{" + pro.proName + r",jdbcType=" + pro.fieldType +r"}"}, </if> </#list> </trim> </insert> <!-- 按需修改--> <update id="updatePrimaryKeySelective" parameterType="${entityPackageName}.${entityName}"> update ${tableName} <set> <#list columns as pro> <#if pro.fieldName != primaryId && pro.fieldName != primaryId> <if test="${pro.proName} != null"> ${pro.fieldName} = ${r"#{" + pro.proName + r",jdbcType=" + pro.fieldType +r"}"}, </if> </#if> </#list> </set> where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"} </update> <!-- 按需批量修改--> <update id="updateBatchByIds" parameterType="java.util.List"> update ${tableName} <trim prefix="set" suffixOverrides=","> <#list columns as pro> <#if pro.fieldName != primaryId && pro.fieldName != primaryId> <trim prefix="${pro.fieldName}=case" suffix="end,"> <foreach collection="list" item="obj" index="index"> <if test="obj.${pro.proName} != null"> when id = ${r"#{" + "obj.id" + r"}"} then ${r"#{obj." + pro.proName + r",jdbcType=" + pro.fieldType +r"}"} </if> </foreach> </trim> </#if> </#list> </trim> where <foreach collection="list" separator="or" item="obj" index="index" > id = ${r"#{" + "obj.id" + r"}"} </foreach> </update> <!-- 删除--> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> delete from ${tableName} where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"} </delete> <!-- 查询详情 --> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long"> select <include refid="Base_Column_List"/> from ${tableName} where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"} </select> <!-- 按需查询 --> <select id="selectByPrimaryKeySelective" resultMap="BaseResultMap" parameterType="${entityPackageName}.${entityName}"> select <include refid="Base_Column_List"/> from ${tableName} </select> <!-- 批量查询--> <select id="selectByIds" resultMap="BaseResultMap" parameterType="java.util.List"> select <include refid="Base_Column_List"/> from ${tableName} <where> <if test="ids != null"> and ${primaryId} in <foreach item="item" index="index" collection="ids" open="(" separator="," close=")"> ${r"#{" + "item" + r"}"} </foreach> </if> </where> </select> <!-- 根据条件查询 --> <select id="selectByMap" resultMap="BaseResultMap" parameterType="java.util.Map"> select <include refid="Base_Column_List"/> from ${tableName} </select> <!-- 查询${entityName}总和 --> <select id="countPage" resultType="int" parameterType="${dtoPackageName}.${dtoName}"> select count(${primaryId}) from ${tableName} </select> <!-- 查询${entityName}列表 --> <select id="selectPage" resultMap="BaseResultMap" parameterType="${dtoPackageName}.${dtoName}"> select <include refid="Base_Column_List"/> from ${tableName} limit ${r"#{" + "start,jdbcType=INTEGER" + r"}"},${r"#{" + "end,jdbcType=INTEGER" + r"}"} </select> </mapper>
- 编写
dao
数据访问模板
package ${daoPackageName}; import com.example.generator.core.BaseMapper; import java.util.List; import ${entityPackageName}.${entityName}; import ${dtoPackageName}.${dtoName}; /** * * @ClassName: ${daoName} * @Description: 数据访问接口 * @author ${authorName} * @date ${currentTime} * */ public interface ${daoName} extends BaseMapper<${entityName}>{ int countPage(${dtoName} ${dtoName?uncap_first}); List<${entityName}> selectPage(${dtoName} ${dtoName?uncap_first}); }
- 编写
service
服务接口模板
package ${servicePackageName}; import com.example.generator.core.BaseService; import com.example.generator.common.Pager; import ${voPackageName}.${voName}; import ${dtoPackageName}.${dtoName}; import ${entityPackageName}.${entityName}; /** * * @ClassName: ${serviceName} * @Description: ${entityName}业务访问接口 * @author ${authorName} * @date ${currentTime} * */ public interface ${serviceName} extends BaseService<${entityName}> { /** * 分页列表查询 * @param request */ Pager<${voName}> getPage(${dtoName} request); }
- 编写
serviceImpl
服务实现类模板
package ${serviceImplPackageName}; import com.example.generator.common.Pager; import com.example.generator.core.BaseServiceImpl; import com.example.generator.test.service.TestEntityService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import ${daoPackageName}.${daoName}; import ${entityPackageName}.${entityName}; import ${dtoPackageName}.${dtoName}; import ${voPackageName}.${voName}; @Service public class ${serviceImplName} extends BaseServiceImpl<${daoName}, ${entityName}> implements ${serviceName} { private static final Logger log = LoggerFactory.getLogger(${serviceImplName}.class); /** * 分页列表查询 * @param request */ public Pager<${voName}> getPage(${dtoName} request) { List<${voName}> resultList = new ArrayList(); int count = super.baseMapper.countPage(request); List<${entityName}> dbList = count > 0 ? super.baseMapper.selectPage(request) : new ArrayList<>(); if(!CollectionUtils.isEmpty(dbList)){ dbList.forEach(source->{ ${voName} target = new ${voName}(); BeanUtils.copyProperties(source, target); resultList.add(target); }); } return new Pager(request.getCurrPage(), request.getPageSize(), count, resultList); } }
- 编写
controller
控制层模板
package ${controllerPackageName}; import com.example.generator.common.IdRequest; import com.example.generator.common.Pager; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; 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.RestController; import java.util.Objects; import ${servicePackageName}.${serviceName}; import ${entityPackageName}.${entityName}; import ${dtoPackageName}.${dtoName}; import ${voPackageName}.${voName}; /** * * @ClassName: ${controllerName} * @Description: 外部访问接口 * @author ${authorName} * @date ${currentTime} * */ @RestController @RequestMapping("/${entityName?uncap_first}") public class ${controllerName} { @Autowired private ${serviceName} ${serviceName?uncap_first}; /** * 分页列表查询 * @param request */ @PostMapping(value = "/getPage") public Pager<${voName}> getPage(@RequestBody ${dtoName} request){ return ${serviceName?uncap_first}.getPage(request); } /** * 查询详情 * @param request */ @PostMapping(value = "/getDetail") public ${voName} getDetail(@RequestBody IdRequest request){ ${entityName} source = ${serviceName?uncap_first}.selectById(request.getId()); if(Objects.nonNull(source)){ ${voName} result = new ${voName}(); BeanUtils.copyProperties(source, result); return result; } return null; } /** * 新增操作 * @param request */ @PostMapping(value = "/save") public void save(${dtoName} request){ ${entityName} entity = new ${entityName}(); BeanUtils.copyProperties(request, entity); ${serviceName?uncap_first}.insert(entity); } /** * 编辑操作 * @param request */ @PostMapping(value = "/edit") public void edit(${dtoName} request){ ${entityName} entity = new ${entityName}(); BeanUtils.copyProperties(request, entity); ${serviceName?uncap_first}.updateById(entity); } /** * 删除操作 * @param request */ @PostMapping(value = "/delete") public void delete(IdRequest request){ ${serviceName?uncap_first}.deleteById(request.getId()); } }
- 编写
entity
实体类模板
package ${entityPackageName}; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; /** * * @ClassName: ${entityName} * @Description: ${tableDes!}实体类 * @author ${authorName} * @date ${currentTime} * */ public class ${entityName} implements Serializable { private static final long serialVersionUID = 1L; <#--属性遍历--> <#list columns as pro> <#--<#if pro.proName != primaryId && pro.proName != 'remarks' && pro.proName != 'createBy' && pro.proName != 'createDate' && pro.proName != 'updateBy' && pro.proName != 'updateDate' && pro.proName != 'delFlag' && pro.proName != 'currentUser' && pro.proName != 'page' && pro.proName != 'sqlMap' && pro.proName != 'isNewRecord' ></#if>--> /** * ${pro.proDes!} */ private ${pro.proType} ${pro.proName}; </#list> <#--属性get||set方法--> <#list columns as pro> public ${pro.proType} get${pro.proName?cap_first}() { return this.${pro.proName}; } public ${entityName} set${pro.proName?cap_first}(${pro.proType} ${pro.proName}) { this.${pro.proName} = ${pro.proName}; return this; } </#list> }
- 编写
dto
实体类模板
package ${dtoPackageName}; import com.example.generator.core.BaseDTO; import java.io.Serializable; /** * @ClassName: ${dtoName} * @Description: 请求实体类 * @author ${authorName} * @date ${currentTime} * */ public class ${dtoName} extends BaseDTO { }