刚入职没多久,连夜手写了一个代码生成器,项目开发速度瞬间屌炸了!(一)

简介: 各位网友,大家好,我是阿粉!最近刚入职一个新团队,还没来得及熟悉业务,甲方爸爸就要求项目要在2个月内完成开发并上线!本想着往后推迟1个月在交付,但是甲方爸爸不同意,只能赶鸭子上架了!然后根据业务需求,设计出了大概30多张表,如果这30多张表,全靠开发人员手写 crud,开发所需的时间肯定会大大的延长,甚至可能直接会影响交付时间!于是就想着,能不能通过代码生成器一键搞定全部的 crud,开发团队只需要根据业务需求编写逻辑代码就可以?

一、简介

各位网友,大家好,我是阿粉!

最近刚入职一个新团队,还没来得及熟悉业务,甲方爸爸就要求项目要在2个月内完成开发并上线!

本想着往后推迟1个月在交付,但是甲方爸爸不同意,只能赶鸭子上架了!

然后根据业务需求,设计出了大概30多张表,如果这30多张表,全靠开发人员手写 crud,开发所需的时间肯定会大大的延长,甚至可能直接会影响交付时间!

于是就想着,能不能通过代码生成器一键搞定全部的 crud,开发团队只需要根据业务需求编写逻辑代码就可以?

本来计划是用mybatis-plus的,但是生成的代码,根据现有的框架标准,很多代码也需要自己改,有些地方还不如自己手写用的舒服,因此就决定手写一套代码生成器!

很多新手会觉得代码生成器很个高深的东西。其实不然,一点都不高深,当你看完本文的时候,你会完全掌握代码生成器的逻辑,甚至可以根据自己的项目情况,进行深度定制。

废话也不多说了,直接代码撸上!

二、实现思路

下面我就以SpringBoot项目为例,数据持久化操作采用Mybatis,数据库采用Mysql,编写一个自动生成增、删、改、查等基础功能的代码生成器,内容包括controllerservicedaoentitydtovo等信息。

实现思路如下:

  • 第一步:获取表字段名称、类型、表注释等信息
  • 第二步:基于 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

0.jpg


同时,获取对应表注释,用于生成备注信息

# 获取对应表注释
SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_sc


1.jpg

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 {
}
相关文章
|
Cloud Native Go Python
面试前夜:最后准备的小贴士
面试前夜:最后准备的小贴士
64 0
|
编译器 C语言 C++
重生之我要学C++第四天
重生之我要学C++第四天
96 0
|
IDE 数据可视化 程序员
3款强大到离谱的电脑软件,个个提效神器,从此远离加班
3款强大到离谱的电脑软件,个个提效神器,从此远离加班
|
机器学习/深度学习 人工智能 自然语言处理
【paddlehubOCR项目】网课手酸酸,眼花花,救星来啦!
大家好这里是三岁,今天给大家带来的是在AiStudio项目平台的一个精选项目,虽然很短,但是效果拔群,使用到了最近特别火的paddleOCR~~~
280 0
【paddlehubOCR项目】网课手酸酸,眼花花,救星来啦!
|
程序员 Android开发 iOS开发
程序员五一修图小贴士
程序员五一修图小贴士
161 0
程序员五一修图小贴士
|
容器
L2-020. 功夫传人
L2-020. 功夫传人
199 0
|
编解码 前端开发 程序员
刚入职的程序员做不好哪些事情容易被开除?
刚入职的程序员做不好哪些事情容易被开除?
851 0
|
程序员
程序人生 - 程序员要学点儿理财知识,而不仅仅是代码技巧
程序人生 - 程序员要学点儿理财知识,而不仅仅是代码技巧
139 0
程序人生 - 程序员要学点儿理财知识,而不仅仅是代码技巧
|
机器学习/深度学习 人工智能 安全
我新入职的“同事”们,既能干脏活累活,又不内卷,真香!
疫情让那些由 AI、ML 和 RPA 驱动的数字员工走向生产线
175 0
我新入职的“同事”们,既能干脏活累活,又不内卷,真香!