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

一、介紹
我記得最早剛步入互聯(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)容包括controller、service、dao、entity、dto、vo等信息。
實現(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};
?#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.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)看到了,在模板中我們用到了BaseMapper、BaseService、BaseServiceImpl等等服務類。
之所以有這三個類,是因為在模板中,我們有大量的相同的方法名包括邏輯也相似,除了所在實體類不一樣以外,其他都一樣,因此我們可以借助泛型類來將這些服務抽成公共的部分。
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?extends?Serializable>?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?extends?Serializable>?ids) ;
????/**
?????*?根據(jù)條件查詢
?????*?@param?columnMap
?????*?@return
?????*/
????List?selectByMap(Map?columnMap) ;
}
BaseServiceImpl,service層的公共方法具體實現(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?extends?Serializable>?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)容不錯的話,記得分享朋友圈讓更多的人知道!


【神秘禮包獲取方式】
