<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【非廣告】你還在手寫crud嗎,看完這篇文章,絕對賺了

          共 10175字,需瀏覽 21分鐘

           ·

          2020-11-22 17:01

          每天早上七點三十,準時推送干貨



          一、介紹

          我記得最早剛步入互聯(lián)網(wǎng)行業(yè)的時候,當時按照 MVC 的思想和模型,每次開發(fā)新功能,會依次編寫 dao、service、controller相關服務類,包括對應的 dto、entity、vo 等等實體類,如果有多張單表,也會重復的編寫相似的代碼,現(xiàn)在回想起來,感覺當時自己好像處于石器時代

          實際上,當仔細的總結(jié)一下,對于任何一張單表的操作,基本都是圍繞增(Create )、刪(Delete )、改(Update )、查(Retrieve )四個方向進行數(shù)據(jù)操作,簡稱 CRUD!

          他們除了表名和存儲空間不一樣,基本的 CRUD 思路基本都是一樣的。

          為了解決這些重復勞動的痛點,業(yè)界逐漸開源了一批代碼生成器,目的也很簡單,就是為了減少手工操作的繁瑣,集中精力在業(yè)務開發(fā)上,提升開發(fā)效率

          而今天,我們所要介紹的也是代碼生成器,很多初學者可能覺得代碼生成器很高深。代碼生成器其實是一個很簡單的東西,一點都不高深。

          當你看完本文的時候,你會完全掌握代碼生成器的邏輯,甚至可以根據(jù)自己的項目情況,進行深度定制

          二、實現(xiàn)思路

          下面我們就以SpringBoot項目為例,數(shù)據(jù)持久化操作采用Mybatis,數(shù)據(jù)庫采用Mysql,編寫一個自動生成增、刪、改、查等基礎功能的代碼生成器,內(nèi)容包括controllerservicedaoentitydtovo等信息。

          實現(xiàn)思路如下:

          • 第一步:獲取表字段名稱、類型、表注釋等信息
          • 第二步:基于 freemarker 模板引擎,編寫相應的模板
          • 第三步:根據(jù)對應的模板,生成相應的 java 代碼

          2.1、獲取表結(jié)構(gòu)

          首先我們創(chuàng)建一張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?'創(chuàng)建時間',
          ??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='測試表';

          表創(chuàng)建完成之后,基于test_db表,我們查詢對應的表結(jié)果字段名稱、類型、備注信息,這些關鍵信息將用于后續(xù)進行代碼生成器所使用

          #?獲取對應表結(jié)構(gòu)
          SELECT?column_name,?data_type,?column_comment?FROM?information_schema.columns?WHERE?table_schema?=?'yjgj_base'?AND?table_name?=?'test_db'

          同時,獲取對應表注釋,用于生成備注信息

          #?獲取對應表注釋
          SELECT?TABLE_COMMENT?FROM?INFORMATION_SCHEMA.TABLES?WHERE?table_schema?=?'yjgj_base'?AND?table_name?=?'test_db'

          2.2、編寫模板

          • 編寫mapper.ftl模板,涵蓋新增、修改、刪除、查詢等信息

          mapper?PUBLIC?"-//mybatis.org//DTD?Mapper?3.0//EN"?"http://mybatis.org/dtd/mybatis-3-mapper.dtd"?>
          <mapper?namespace="${daoPackageName}.${daoName}">

          ?
          ?<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>

          ?
          ?<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>

          ?
          ?<select?id="countPage"?resultType="int"?parameterType="${dtoPackageName}.${dtoName}">
          ??select?count(${primaryId})
          ??from?${tableName}
          ?select>

          ?
          ?<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.ftl數(shù)據(jù)訪問模板
          package?${daoPackageName};

          import?com.example.generator.core.BaseMapper;
          import?java.util.List;
          import?${entityPackageName}.${entityName};
          import?${dtoPackageName}.${dtoName};

          /**
          *
          *?@ClassName:?${daoName}
          *?@Description:?數(shù)據(jù)訪問接口
          *?@author?${authorName}
          *?@date?${currentTime}
          *
          */

          public?interface?${daoName}?extends?BaseMapper<${entityName}>{

          ?int?countPage(${dtoName}?${dtoName?uncap_first});

          ?List<${entityName}>?selectPage(${dtoName}?${dtoName?uncap_first});
          }
          • 編寫service.ftl服務接口模板
          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}業(yè)務訪問接口
          ?*?@author?${authorName}
          ?*?@date?${currentTime}
          ?*
          ?*/

          public?interface?${serviceName}?extends?BaseService<${entityName}>?{

          ?/**
          ??*?分頁列表查詢
          ??*?@param?request
          ??*/

          ?Pager<${voName}>?getPage(${dtoName}?request);
          }
          • 編寫serviceImpl.ftl服務實現(xiàn)類模板
          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.ftl控制層模板
          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.ftl實體類模板
          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};
          ?

          ?<#--屬性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;
          ?}
          ?
          }
          • 編寫dto.ftl實體類模板
          package?${dtoPackageName};

          import?com.example.generator.core.BaseDTO;
          import?java.io.Serializable;

          /**
          ?*?@ClassName:?${dtoName}
          ?*?@Description:?請求實體類
          ?*?@author?${authorName}
          ?*?@date?${currentTime}
          ?*
          ?*/

          public?class?${dtoName}?extends?BaseDTO?{

          }
          • 編寫vo.ftl視圖實體類模板
          package?${voPackageName};

          import?java.io.Serializable;

          /**
          ?*?@ClassName:?${voName}
          ?*?@Description:?返回視圖實體類
          ?*?@author?${authorName}
          ?*?@date?${currentTime}
          ?*
          ?*/

          public?class?${voName}?implements?Serializable?{

          ?private?static?final?long?serialVersionUID?=?1L;
          }

          可能細心的網(wǎng)友已經(jīng)看到了,在模板中我們用到了BaseMapperBaseServiceBaseServiceImpl等等服務類。

          之所以有這三個類,是因為在模板中,我們有大量的相同的方法名包括邏輯也相似,除了所在實體類不一樣以外,其他都一樣,因此我們可以借助泛型類來將這些服務抽成公共的部分。

          • BaseMapper,主要負責將dao層的公共方法抽出來
          package?com.example.generator.core;

          import?org.apache.ibatis.annotations.Param;

          import?java.io.Serializable;
          import?java.util.List;
          import?java.util.Map;

          /**
          ?*?@author?pzblog
          ?*?@Description
          ?*?@since?2020-11-11
          ?*/

          public?interface?BaseMapper<T>?{

          ????/**
          ?????*?批量插入
          ?????*?@param?list
          ?????*?@return
          ?????*/

          ????int?insertList(@Param("list")?List?list);

          ????/**
          ?????*?按需插入一條記錄
          ?????*?@param?entity
          ?????*?@return
          ?????*/

          ????int?insertPrimaryKeySelective(T?entity);

          ????/**
          ?????*?按需修改一條記錄(通過主鍵ID)
          ?????*?@return
          ?????*/

          ????int?updatePrimaryKeySelective(T?entity);

          ????/**
          ?????*?批量按需修改記錄(通過主鍵ID)
          ?????*?@param?list
          ?????*?@return
          ?????*/

          ????int?updateBatchByIds(@Param("list")?List?list);

          ????/**
          ?????*?根據(jù)ID刪除
          ?????*?@param?id?主鍵ID
          ?????*?@return
          ?????*/

          ????int?deleteByPrimaryKey(Serializable?id);

          ????/**
          ?????*?根據(jù)ID查詢
          ?????*?@param?id?主鍵ID
          ?????*?@return
          ?????*/

          ????T?selectByPrimaryKey(Serializable?id);

          ????/**
          ?????*?按需查詢
          ?????*?@param?entity
          ?????*?@return
          ?????*/

          ????List?selectByPrimaryKeySelective(T?entity);

          ????/**
          ?????*?批量查詢
          ?????*?@param?ids?主鍵ID集合
          ?????*?@return
          ?????*/

          ????List?selectByIds(@Param("ids")?List?ids);

          ????/**
          ?????*?查詢(根據(jù)?columnMap?條件)
          ?????*?@param?columnMap?表字段?map?對象
          ?????*?@return
          ?????*/

          ????List?selectByMap(Map?columnMap);
          }
          • BaseService,主要負責將service層的公共方法抽出來
          package?com.example.generator.core;

          import?java.io.Serializable;
          import?java.util.List;
          import?java.util.Map;

          /**
          ?*?@author?pzblog
          ?*?@Description?服務類
          ?*?@since?2020-11-11
          ?*/

          public?interface?BaseService<T>?{

          ????/**
          ?????*?新增
          ?????*?@param?entity
          ?????*?@return?boolean
          ?????*/

          ????boolean?insert(T?entity);

          ????/**
          ?????*?批量新增
          ?????*?@param?list
          ?????*?@return?boolean
          ?????*/

          ????boolean?insertList(List?list);

          ????/**
          ?????*?通過ID修改記錄(如果想全部更新,只需保證字段都不為NULL)
          ?????*?@param?entity
          ?????*?@return?boolean
          ?????*/

          ????boolean?updateById(T?entity);

          ????/**
          ?????*?通過ID批量修改記錄(如果想全部更新,只需保證字段都不為NULL)
          ?????*?@param?list
          ?????*?@return?boolean
          ?????*/

          ????boolean?updateBatchByIds(List?list);

          ????/**
          ?????*?根據(jù)ID刪除
          ?????*?@param?id?主鍵ID
          ?????*?@return?boolean
          ?????*/

          ????boolean?deleteById(Serializable?id);

          ????/**
          ?????*?根據(jù)ID查詢
          ?????*?@param?id?主鍵ID
          ?????*?@return
          ?????*/

          ????T?selectById(Serializable?id);

          ????/**
          ?????*?按需查詢
          ?????*?@param?entity
          ?????*?@return
          ?????*/

          ????List?selectByPrimaryKeySelective(T?entity);

          ????/**
          ?????*?批量查詢
          ?????*?@param?ids
          ?????*?@return
          ?????*/

          ????List?selectByIds(List?ids);

          ????/**
          ?????*?根據(jù)條件查詢
          ?????*?@param?columnMap
          ?????*?@return
          ?????*/

          ????List?selectByMap(Map?columnMap);

          }
          • BaseServiceImplservice層的公共方法具體實現(xiàn)類
          package?com.example.generator.core;

          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.transaction.annotation.Transactional;

          import?java.io.Serializable;
          import?java.util.List;
          import?java.util.Map;

          /**
          ?*?@author?pzblog
          ?*?@Description?實現(xiàn)類(?泛型說明:M 是 mapper 對象,T 是實體)
          ?*?@since?2020-11-11
          ?*/

          public?abstract?class?BaseServiceImpl<M?extends?BaseMapper<T>,?T>?implements?BaseService<T>{

          ????@Autowired
          ????protected?M?baseMapper;

          ????/**
          ?????*?新增
          ?????*?@param?entity
          ?????*?@return?boolean
          ?????*/

          ????@Override
          ????@Transactional(rollbackFor?=?{Exception.class})
          ????public?boolean?insert(T?entity)
          {
          ????????return?returnBool(baseMapper.insertPrimaryKeySelective(entity));
          ????}

          ????/**
          ?????*?批量新增
          ?????*?@param?list
          ?????*?@return?boolean
          ?????*/

          ????@Override
          ????@Transactional(rollbackFor?=?{Exception.class})
          ????public?boolean?insertList(List<T>?list)
          {
          ????????return?returnBool(baseMapper.insertList(list));
          ????}

          ????/**
          ?????*?通過ID修改記錄(如果想全部更新,只需保證字段都不為NULL)
          ?????*?@param?entity
          ?????*?@return?boolean
          ?????*/

          ????@Override
          ????@Transactional(rollbackFor?=?{Exception.class})
          ????public?boolean?updateById(T?entity)
          {
          ????????return?returnBool(baseMapper.updatePrimaryKeySelective(entity));
          ????}

          ????/**
          ?????*?通過ID批量修改記錄(如果想全部更新,只需保證字段都不為NULL)
          ?????*?@param?list
          ?????*?@return?boolean
          ?????*/

          ????@Override
          ????@Transactional(rollbackFor?=?{Exception.class})
          ????public?boolean?updateBatchByIds(List<T>?list)
          {
          ????????return?returnBool(baseMapper.updateBatchByIds(list));
          ????}

          ????/**
          ?????*?根據(jù)ID刪除
          ?????*?@param?id?主鍵ID
          ?????*?@return?boolean
          ?????*/

          ????@Override
          ????@Transactional(rollbackFor?=?{Exception.class})
          ????public?boolean?deleteById(Serializable?id)
          {
          ????????return?returnBool(baseMapper.deleteByPrimaryKey(id));
          ????}

          ????/**
          ?????*?根據(jù)ID查詢
          ?????*?@param?id?主鍵ID
          ?????*?@return
          ?????*/

          ????@Override
          ????public?T?selectById(Serializable?id){
          ????????return?baseMapper.selectByPrimaryKey(id);
          ????}

          ????/**
          ?????*?按需查詢
          ?????*?@param?entity
          ?????*?@return
          ?????*/

          ????@Override
          ????public?List?selectByPrimaryKeySelective(T?entity){
          ????????return?baseMapper.selectByPrimaryKeySelective(entity);
          ????}

          ????/**
          ?????*?批量查詢
          ?????*?@param?ids
          ?????*?@return
          ?????*/

          ????@Override
          ????public?List?selectByIds(List?ids){
          ????????return?baseMapper.selectByIds(ids);
          ????}

          ????/**
          ?????*?根據(jù)條件查詢
          ?????*?@param?columnMap
          ?????*?@return
          ?????*/

          ????@Override
          ????public?List?selectByMap(Map?columnMap){
          ????????return?baseMapper.selectByMap(columnMap);
          ????}

          ????/**
          ?????*?判斷數(shù)據(jù)庫操作是否成功
          ?????*?@param?result?數(shù)據(jù)庫操作返回影響條數(shù)
          ?????*?@return?boolean
          ?????*/

          ????protected?boolean?returnBool(Integer?result)?{
          ????????return?null?!=?result?&&?result?>=?1;
          ????}

          }

          在此,還封裝來其他的類,例如 dto 公共類BaseDTO,分頁類Pager,還有 id 請求類IdRequest

          • BaseDTO公共類
          public?class?BaseDTO?implements?Serializable?{

          ????/**
          ?????*?請求token
          ?????*/

          ????private?String?token;

          ????/**
          ?????*?當前頁數(shù)
          ?????*/

          ????private?Integer?currPage?=?1;

          ????/**
          ?????*?每頁記錄數(shù)
          ?????*/

          ????private?Integer?pageSize?=?20;

          ????/**
          ?????*?分頁參數(shù)(第幾行)
          ?????*/

          ????private?Integer?start;

          ????/**
          ?????*?分頁參數(shù)(行數(shù))
          ?????*/

          ????private?Integer?end;

          ????/**
          ?????*?登錄人ID
          ?????*/

          ????private?String?loginUserId;

          ????/**
          ?????*?登錄人名稱
          ?????*/

          ????private?String?loginUserName;

          ????public?String?getToken()?{
          ????????return?token;
          ????}

          ????public?BaseDTO?setToken(String?token)?{
          ????????this.token?=?token;
          ????????return?this;
          ????}

          ????public?Integer?getCurrPage()?{
          ????????return?currPage;
          ????}

          ????public?BaseDTO?setCurrPage(Integer?currPage)?{
          ????????this.currPage?=?currPage;
          ????????return?this;
          ????}

          ????public?Integer?getPageSize()?{
          ????????return?pageSize;
          ????}

          ????public?BaseDTO?setPageSize(Integer?pageSize)?{
          ????????this.pageSize?=?pageSize;
          ????????return?this;
          ????}

          ????public?Integer?getStart()?{
          ????????if?(this.currPage?!=?null?&&?this.currPage?>?0)?{
          ????????????start?=?(currPage?-?1)?*?getPageSize();
          ????????????return?start;
          ????????}
          ????????return?start?==?null???0?:?start;
          ????}

          ????public?BaseDTO?setStart(Integer?start)?{
          ????????this.start?=?start;
          ????????return?this;
          ????}

          ????public?Integer?getEnd()?{
          ????????return?getPageSize();
          ????}

          ????public?BaseDTO?setEnd(Integer?end)?{
          ????????this.end?=?end;
          ????????return?this;
          ????}

          ????public?String?getLoginUserId()?{
          ????????return?loginUserId;
          ????}

          ????public?BaseDTO?setLoginUserId(String?loginUserId)?{
          ????????this.loginUserId?=?loginUserId;
          ????????return?this;
          ????}

          ????public?String?getLoginUserName()?{
          ????????return?loginUserName;
          ????}

          ????public?BaseDTO?setLoginUserName(String?loginUserName)?{
          ????????this.loginUserName?=?loginUserName;
          ????????return?this;
          ????}

          }
          • Pager分頁類
          public?class?Pager<T?extends?Serializable>?implements?Serializable?{

          ????private?static?final?long?serialVersionUID?=?-6557244954523041805L;

          ????/**
          ?????*?當前頁數(shù)
          ?????*/

          ????private?int?currPage;

          ????/**
          ?????*?每頁記錄數(shù)
          ?????*/

          ????private?int?pageSize;

          ????/**
          ?????*?總頁數(shù)
          ?????*/

          ????private?int?totalPage;

          ????/**
          ?????*?總記錄數(shù)
          ?????*/

          ????private?int?totalCount;

          ????/**
          ?????*?列表數(shù)據(jù)
          ?????*/

          ????private?List?list;

          ????public?Pager(int?currPage,?int?pageSize)?{
          ????????this.currPage?=?currPage;
          ????????this.pageSize?=?pageSize;
          ????}

          ????public?Pager(int?currPage,?int?pageSize,?int?totalCount,?List?list)?{
          ????????this.currPage?=?currPage;
          ????????this.pageSize?=?pageSize;
          ????????this.totalPage?=?(int)?Math.ceil((double)?totalCount?/?pageSize);;
          ????????this.totalCount?=?totalCount;
          ????????this.list?=?list;
          ????}

          ????public?int?getCurrPage()?{
          ????????return?currPage;
          ????}

          ????public?Pager?setCurrPage(int?currPage)?{
          ????????this.currPage?=?currPage;
          ????????return?this;
          ????}

          ????public?int?getPageSize()?{
          ????????return?pageSize;
          ????}

          ????public?Pager?setPageSize(int?pageSize)?{
          ????????this.pageSize?=?pageSize;
          ????????return?this;
          ????}

          ????public?int?getTotalPage()?{
          ????????return?totalPage;
          ????}

          ????public?Pager?setTotalPage(int?totalPage)?{
          ????????this.totalPage?=?totalPage;
          ????????return?this;
          ????}

          ????public?int?getTotalCount()?{
          ????????return?totalCount;
          ????}

          ????public?Pager?setTotalCount(int?totalCount)?{
          ????????this.totalCount?=?totalCount;
          ????????return?this;
          ????}

          ????public?List?getList()?{
          ????????return?list;
          ????}

          ????public?Pager?setList(List?list)?{
          ????????this.list?=?list;
          ????????return?this;
          ????}
          }

          • IdRequest公共請求類
          public?class?IdRequest?extends?BaseDTO?{

          ????private?Long?id;

          ????public?Long?getId()?{
          ????????return?id;
          ????}

          ????public?IdRequest?setId(Long?id)?{
          ????????this.id?=?id;
          ????????return?this;
          ????}
          }

          2.3、編寫代碼生成器

          前兩部分主要介紹的是如何獲取對應的表結(jié)構(gòu),以及代碼器運行之前的準備工作。

          其實代碼生成器,很簡單,其實就是一個main方法,沒有想象中的那么復雜。

          處理思路也很簡單,過程如下:

          • 1、定義基本變量,例如包名路徑、模塊名、表名、轉(zhuǎn)換后的實體類、以及數(shù)據(jù)庫連接配置,我們可以將其寫入配置文件
          • 2、讀取配置文件,封裝對應的模板中定義的變量
          • 3、根據(jù)對應的模板文件和變量,生成對應的java文件
          2.3.1、創(chuàng)建配置文件,定義變量

          小編我用的是application.properties配置文件來定義變量,這個沒啥規(guī)定,你也可以自定義文件名,內(nèi)容如下:

          #包前綴
          packageNamePre=com.example.generator
          #模塊名稱
          moduleName=test
          #表
          tableName=test_db
          #實體類名稱
          entityName=TestEntity
          #主鍵ID
          primaryId=id
          #作者
          authorName=pzblog
          #數(shù)據(jù)庫名稱
          databaseName=yjgj_base

          #數(shù)據(jù)庫服務器IP地址
          ipName=127.0.0.1
          #數(shù)據(jù)庫服務器端口
          portName=3306
          #用戶名
          userName=root
          #密碼
          passWord=123456

          #文件輸出路徑,支持自定義輸出路徑,如果為空,默認取當前工程的src/main/java路徑
          outUrl=
          2.3.2、根據(jù)模板生成對應的java代碼
          • 首先,讀取配置文件變量
          public?class?SystemConstant?{

          ????private?static?Properties?properties?=?new?Properties();

          ????static?{
          ????????try?{
          ????????????//?加載上傳文件設置參數(shù):配置文件
          ????????????properties.load(SystemConstant.class.getClassLoader().getResourceAsStream("application.properties"));
          ????????}?catch?(IOException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}

          ????public?static?final?String?tableName?=?properties.getProperty("tableName");
          ????public?static?final?String?entityName?=?properties.getProperty("entityName");
          ????public?static?final?String?packageNamePre?=?properties.getProperty("packageNamePre");
          ????public?static?final?String?outUrl?=?properties.getProperty("outUrl");
          ????public?static?final?String?databaseName?=?properties.getProperty("databaseName");
          ????public?static?final?String?ipName?=?properties.getProperty("ipName");
          ????public?static?final?String?portName?=?properties.getProperty("portName");
          ????public?static?final?String?userName?=?properties.getProperty("userName");
          ????public?static?final?String?passWord?=?properties.getProperty("passWord");
          ????public?static?final?String?authorName?=?properties.getProperty("authorName");

          ????public?static?final?String?primaryId?=?properties.getProperty("primaryId");

          ????public?static?final?String?moduleName?=?properties.getProperty("moduleName");
          }
          • 然后,封裝對應的模板中定義的變量
          public?class?CodeService?{

          ????public?void?generate(Map?templateData)?{
          ????????//包前綴
          ????????String?packagePreAndModuleName?=?getPackagePreAndModuleName(templateData);

          ????????//支持對應實體插入在前面,需要帶上%s
          ????????templateData.put("entityPackageName",?String.format(packagePreAndModuleName?+?".entity",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("dtoPackageName",?String.format(packagePreAndModuleName?+?".dto",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("voPackageName",?String.format(packagePreAndModuleName?+?".vo",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("daoPackageName",?String.format(packagePreAndModuleName?+?".dao",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("mapperPackageName",?packagePreAndModuleName?+?".mapper");


          ????????templateData.put("servicePackageName",?String.format(packagePreAndModuleName?+?".service",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("serviceImplPackageName",?String.format(packagePreAndModuleName?+?".service.impl",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("controllerPackageName",?String.format(packagePreAndModuleName?+?".web",
          ????????????????templateData.get("entityName").toString().toLowerCase()));

          ????????templateData.put("apiTestPackageName",?String.format(packagePreAndModuleName?+?".junit",
          ????????????????templateData.get("entityName").toString().toLowerCase()));


          ????????templateData.put("currentTime",?new?SimpleDateFormat("yyyy-MM-dd").format(new?Date()));

          ????????//======================生成文件配置======================
          ????????try?{
          ????????????//?生成Entity
          ????????????String?entityName?=?String.format("%s",?templateData.get("entityName").toString());
          ????????????generateFile("entity.ftl",?templateData,?templateData.get("entityPackageName").toString(),?entityName+".java");

          ????????????//?生成dto
          ????????????String?dtoName?=?String.format("%sDTO",?templateData.get("entityName").toString());
          ????????????templateData.put("dtoName",?dtoName);
          ????????????generateFile("dto.ftl",?templateData,?templateData.get("dtoPackageName").toString(),
          ????????????????????dtoName?+?".java");

          ????????????//?生成VO
          ????????????String?voName?=?String.format("%sVO",?templateData.get("entityName").toString());
          ????????????templateData.put("voName",?voName);
          ????????????generateFile("vo.ftl",?templateData,?templateData.get("voPackageName").toString(),
          ????????????????????voName?+?".java");

          ????????????//?生成DAO
          ????????????String?daoName?=?String.format("%sDao",?templateData.get("entityName").toString());
          ????????????templateData.put("daoName",?daoName);
          ????????????generateFile("dao.ftl",?templateData,?templateData.get("daoPackageName").toString(),
          ????????????????????daoName?+?".java");

          ????????????//?生成Mapper
          ????????????String?mapperName?=?String.format("%sMapper",?templateData.get("entityName").toString());
          ????????????generateFile("mapper.ftl",?templateData,?templateData.get("mapperPackageName").toString(),
          ????????????????????mapperName+".xml");


          ????????????//?生成Service
          ????????????String?serviceName?=?String.format("%sService",?templateData.get("entityName").toString());
          ????????????templateData.put("serviceName",?serviceName);
          ????????????generateFile("service.ftl",?templateData,?templateData.get("servicePackageName").toString(),
          ????????????????????serviceName?+?".java");

          ????????????//?生成ServiceImpl
          ???String?serviceImplName?=?String.format("%sServiceImpl",?templateData.get("entityName").toString());
          ???templateData.put("serviceImplName",?serviceImplName);
          ???generateFile("serviceImpl.ftl",?templateData,?templateData.get("serviceImplPackageName").toString(),
          ????????????????????serviceImplName?+?".java");

          ????????????//?生成Controller
          ???String?controllerName?=?String.format("%sController",?templateData.get("entityName").toString());
          ???templateData.put("controllerName",?controllerName);
          ???generateFile("controller.ftl",?templateData,?templateData.get("controllerPackageName").toString(),
          ????????????????????controllerName?+?".java");

          //???//?生成junit測試類
          //????????????String?apiTestName?=?String.format("%sApiTest",?templateData.get("entityName").toString());
          //????????????templateData.put("apiTestName",?apiTestName);
          //????????????generateFile("test.ftl",?templateData,?templateData.get("apiTestPackageName").toString(),
          //????????????????????apiTestName?+?".java");

          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}

          ????/**
          ?????*?生成文件
          ?????*?@param?templateName?模板名稱
          ?????*?@param?templateData?參數(shù)名
          ?????*?@param?packageName?包名
          ?????*?@param?fileName?文件名
          ?????*/

          ????public?void?generateFile(String?templateName,?Map?templateData,?String?packageName,?String?fileName)?{
          ????????templateData.put("fileName",?fileName);

          ????????DaseService?dbService?=?new?DaseService(templateData);

          ????????//?獲取數(shù)據(jù)庫參數(shù)
          ????????if("entity.ftl".equals(templateName)?||?"mapper.ftl".equals(templateName)){
          ????????????dbService.getAllColumns(templateData);
          ????????}
          ????????try?{
          ????????????//?默認生成文件的路徑
          ????????????FreeMakerUtil?freeMakerUtil?=?new?FreeMakerUtil();
          ????????????freeMakerUtil.generateFile(templateName,?templateData,?packageName,?fileName);
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}

          ????/**
          ?????*?封裝包名前綴
          ?????*?@return
          ?????*/

          ????private?String?getPackagePreAndModuleName(Map?templateData){
          ????????String?packageNamePre?=?templateData.get("packageNamePre").toString();
          ????????String?moduleName?=?templateData.get("moduleName").toString();
          ????????if(StringUtils.isNotBlank(moduleName)){
          ????????????return?packageNamePre?+?"."?+?moduleName;
          ????????}
          ????????return?packageNamePre;
          ????}

          }
          • 接著,獲取模板文件,并生成相應的模板文件
          public?class?FreeMakerUtil?{


          ????/**
          ?????*?根據(jù)Freemark模板,生成文件
          ?????*?@param?templateName:模板名
          ?????*?@param?root:數(shù)據(jù)原型
          ?????*?@throws?Exception
          ?????*/

          ????public?void?generateFile(String?templateName,?Map?root,?String?packageName,?String?fileName)?throws?Exception?{
          ????????FileOutputStream?fos=null;
          ????????Writer?out?=null;
          ????????try?{
          ????????????//?通過一個文件輸出流,就可以寫到相應的文件中,此處用的是絕對路徑
          ????????????String?entityName?=?(String)?root.get("entityName");
          ????????????String?fileFullName?=?String.format(fileName,?entityName);
          ????????????packageName?=?String.format(packageName,?entityName.toLowerCase());

          ????????????String?fileStylePackageName?=?packageName.replaceAll("\\.",?"/");
          ????????????File?file?=?new?File(root.get("outUrl").toString()?+?"/"?+?fileStylePackageName?+?"/"?+?fileFullName);
          ????????????if?(!file.getParentFile().exists())?{
          ????????????????file.getParentFile().mkdirs();
          ????????????}
          ????????????file.createNewFile();

          ????????????Template?template?=?getTemplate(templateName);
          ????????????fos?=?new?FileOutputStream(file);
          ????????????out?=?new?OutputStreamWriter(fos);
          ????????????template.process(root,?out);
          ????????????out.flush();
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}?finally?{
          ????????????try?{
          ????????????????if?(fos?!=?null){
          ????????????????????fos.close();
          ????????????????}
          ????????????????if(out?!=?null){
          ????????????????????out.close();
          ????????????????}
          ????????????}?catch?(IOException?e)?{
          ????????????????e.printStackTrace();
          ????????????}
          ????????}
          ????}

          ????/**
          ?????*
          ?????*?獲取模板文件
          ?????*
          ?????*?@param?name
          ?????*?@return
          ?????*/

          ????public?Template?getTemplate(String?name)?{
          ????????try?{
          ????????????Configuration?cfg?=?new?Configuration(Configuration.VERSION_2_3_23);
          ????????????cfg.setClassForTemplateLoading(this.getClass(),?"/ftl");
          ????????????Template?template?=?cfg.getTemplate(name);
          ????????????return?template;
          ????????}?catch?(IOException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?null;
          ????}
          }
          • 最后,我們編寫一個main方法,看看運行之后的效果
          public?class?GeneratorMain?{

          ????public?static?void?main(String[]?args)?{
          ????????System.out.println("生成代碼start......");

          ????????//獲取頁面或者配置文件的參數(shù)
          ????????Map?templateData?=?new?HashMap();
          ????????templateData.put("tableName",?SystemConstant.tableName);
          ????????System.out.println("表名=="+?SystemConstant.tableName);

          ????????templateData.put("entityName",?SystemConstant.entityName);
          ????????System.out.println("實體類名稱=="+?SystemConstant.entityName);

          ????????templateData.put("packageNamePre",?SystemConstant.packageNamePre);
          ????????System.out.println("包名前綴=="+?SystemConstant.packageNamePre);

          ????????//支持自定義輸出路徑
          ????????if(StringUtils.isNotBlank(SystemConstant.outUrl)){
          ????????????templateData.put("outUrl",?SystemConstant.outUrl);
          ????????}?else?{
          ????????????String?path?=?GeneratorMain.class.getClassLoader().getResource("").getPath()?+?"../../src/main/java";
          ????????????templateData.put("outUrl",?path);
          ????????}
          ????????System.out.println("生成文件路徑為=="+?templateData.get("outUrl"));

          ????????templateData.put("authorName",?SystemConstant.authorName);
          ????????System.out.println("以后代碼出問題找=="+?SystemConstant.authorName);


          ????????templateData.put("databaseName",?SystemConstant.databaseName);
          ????????templateData.put("ipName",?SystemConstant.ipName);
          ????????templateData.put("portName",?SystemConstant.portName);
          ????????templateData.put("userName",?SystemConstant.userName);
          ????????templateData.put("passWord",?SystemConstant.passWord);

          ????????//主鍵ID
          ????????templateData.put("primaryId",?SystemConstant.primaryId);

          ????????//模塊名稱
          ????????templateData.put("moduleName",?SystemConstant.moduleName);
          ????????CodeService?dataService?=?new?CodeService();

          ????????try?{
          ????????????//生成代碼文件
          ????????????dataService.generate(templateData);
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}

          ????????System.out.println("生成代碼end......");
          ????}
          }

          結(jié)果如下:

          • 生成的 Controller 層代碼如下
          /**
          ?*
          ?*?@ClassName:?TestEntityController
          ?*?@Description:?外部訪問接口
          ?*?@author?pzblog
          ?*?@date?2020-11-16
          ?*
          ?*/

          @RestController
          @RequestMapping("/testEntity")
          public?class?TestEntityController?{

          ?@Autowired
          ?private?TestEntityService?testEntityService;

          ?/**
          ??*?分頁列表查詢
          ??*?@param?request
          ??*/

          ?@PostMapping(value?=?"/getPage")
          ?public?Pager?getPage(@RequestBody?TestEntityDTO?request){
          ??return?testEntityService.getPage(request);
          ?}

          ?/**
          ??*?查詢詳情
          ??*?@param?request
          ??*/

          ?@PostMapping(value?=?"/getDetail")
          ?public?TestEntityVO?getDetail(@RequestBody?IdRequest?request){
          ??TestEntity?source?=?testEntityService.selectById(request.getId());
          ??if(Objects.nonNull(source)){
          ???TestEntityVO?result?=?new?TestEntityVO();
          ???BeanUtils.copyProperties(source,?result);
          ???return?result;
          ??}
          ??return?null;
          ?}

          ?/**
          ??*?新增操作
          ??*?@param?request
          ??*/

          ?@PostMapping(value?=?"/save")
          ?public?void?save(TestEntityDTO?request){
          ??TestEntity?entity?=?new?TestEntity();
          ??BeanUtils.copyProperties(request,?entity);
          ??testEntityService.insert(entity);
          ?}

          ?/**
          ??*?編輯操作
          ??*?@param?request
          ??*/

          ?@PostMapping(value?=?"/edit")
          ?public?void?edit(TestEntityDTO?request){
          ??TestEntity?entity?=?new?TestEntity();
          ??BeanUtils.copyProperties(request,?entity);
          ??testEntityService.updateById(entity);
          ?}

          ?/**
          ??*?刪除操作
          ??*?@param?request
          ??*/

          ?@PostMapping(value?=?"/delete")
          ?public?void?delete(IdRequest?request){
          ??testEntityService.deleteById(request.getId());
          ?}
          }

          至此,一張單表的90%的基礎工作量全部開發(fā)完畢!

          三、總結(jié)

          代碼生成器,在實際的項目開發(fā)中應用非常的廣,本文主要以freemaker模板引擎為基礎,開發(fā)的一套全自動代碼生成器,一張單表的CRUD,只需要5秒鐘就可以完成!

          最后多說一句,如果你是項目負責人,那么代碼生成器會是一個比較好的提升項目開發(fā)效率的工具,希望能幫到各位


          PS公號內(nèi)回復「Python」即可進入Python 新手學習交流群,一起?100 天計劃!


          老規(guī)矩,兄弟們還記得么,右下角的 “在看” 點一下如果感覺文章內(nèi)容不錯的話,記得分享朋友圈讓更多的人知道!

          神秘禮包獲取方式

          識別文末二維碼,回復:1024

          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩一区二区三区无码视频 | 大香蕉九九 | 日韩无码字幕 | 毛片操逼视频 | 高清毛片AAAAAAAAA片 |