飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

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

时间:2022-02-10  作者:dxflqm  
如果你想针对当前的项目开发一套定制化代码生成器,本文将给你答案!

一、简介

最近刚入职一个新团队,还没来得及熟悉业务,甲方爸爸就要求项目要在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 域名mns WHERE table_schema = \'yjgj_base\' AND table_name = \'test_db\'

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

# 获取对应表注释
SELECT TABLE_COMMENT FROM 域名ES WHERE table_schema = \'yjgj_base\' AND table_name = \'test_db\'

2.2、编写模板

  • 编写mapper模板,涵盖新增、修改、删除、查询等信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//域名//DTD Mapper 3.0//EN" "http://域名/dtd/mybatis-3-域名" >
<mapper namespace="${daoPackageName}.${daoName}">

	<!--BaseResultMap-->
	<resultMap id="BaseResultMap" type="${entityPackageName}.${entityName}">
        <#list columns as pro>
            <#if 域名ame == primaryId>
				<id column="${primaryId}" property="${primaryId}" jdbcType="${域名dType}"/>
            <#else>
				<result column="${域名dName}" property="${域名ame}" jdbcType="${域名dType}"/>
            </#if>
        </#list>
	</resultMap>

	<!--Base_Column_List-->
	<sql id="Base_Column_List">
        <#list columns as pro>
            <#if pro_index == 0>${域名dName}<#else>,${域名dName}</#if>
        </#list>
	</sql>

	<!--批量插入-->
	<insert id="insertList" parameterType="域名">
		insert into ${tableName} (
        <#list columns as pro>
            <#if pro_index == 0>${域名dName},<#elseif pro_index == 1>${域名dName}<#else>,${域名dName}</#if>
        </#list>
		)
		values
		<foreach collection ="list" item="obj" separator =",">
			<trim prefix=" (" suffix=")" suffixOverrides=",">
                <#list columns as pro>
                    ${r"#{obj." + 域名ame + r"}"},
                </#list>
			</trim>
		</foreach >
	</insert>

	<!--按需新增-->
	<insert id="insertPrimaryKeySelective" parameterType="${entityPackageName}.${entityName}">
		insert into ${tableName}
		<trim prefix="(" suffix=")" suffixOverrides=",">
            <#list columns as pro>
				<if test="${域名ame} != null">
                    ${域名dName},
				</if>
            </#list>
		</trim>
		<trim prefix="values (" suffix=")" suffixOverrides=",">
            <#list columns as pro>
				<if test="${域名ame} != null">
                    ${r"#{" + 域名ame + r",jdbcType=" + 域名dType +r"}"},
				</if>
            </#list>
		</trim>
	</insert>

	<!-- 按需修改-->
	<update id="updatePrimaryKeySelective" parameterType="${entityPackageName}.${entityName}">
		update ${tableName}
		<set>
            <#list columns as pro>
                <#if 域名dName != primaryId && 域名dName != primaryId>
					<if test="${域名ame} != null">
                        ${域名dName} = ${r"#{" + 域名ame + r",jdbcType=" + 域名dType +r"}"},
					</if>
                </#if>
            </#list>
		</set>
		where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"}
	</update>

	<!-- 按需批量修改-->
	<update id="updateBatchByIds" parameterType="域名">
		update ${tableName}
		<trim prefix="set" suffixOverrides=",">
            <#list columns as pro>
                <#if 域名dName != primaryId && 域名dName != primaryId>
					<trim prefix="${域名dName}=case" suffix="end,">
						<foreach collection="list" item="obj" index="index">
							<if test="obj.${域名ame} != null">
								when id = ${r"#{" + "域名" + r"}"}
								then  ${r"#{obj." + 域名ame + r",jdbcType=" + 域名dType +r"}"}
							</if>
						</foreach>
					</trim>
                </#if>
            </#list>
		</trim>
		where
		<foreach collection="list" separator="or" item="obj" index="index" >
			id = ${r"#{" + "域名" + r"}"}
		</foreach>
	</update>

	<!-- 删除-->
	<delete id="deleteByPrimaryKey" parameterType="域名">
		delete from ${tableName}
		where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"}
	</delete>

	<!-- 查询详情 -->
	<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="域名">
		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="域名">
		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="域名">
		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 域名域名Mapper;
import 域名;
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 域名域名Service;
import 域名域名r;
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 域名域名r;
import 域名域名ServiceImpl;
import 域名域名域名EntityService;
import 域名域名Utils;
import 域名域名ice;
import 域名.CollectionUtils;
import 域名er;
import 域名erFactory;

import 域名yList;
import 域名;


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 = 域名ogger(${serviceImplName}.class);

	/**
	 * 分页列表查询
	 * @param request
	 */
	public Pager<${voName}> getPage(${dtoName} request) {
		List<${voName}> resultList = new ArrayList();
		int count = 域名tPage(request);
		List<${entityName}> dbList = count > 0 ? 域名ctPage(request) : new ArrayList<>();
		if(!域名pty(dbList)){
			域名ach(source->{
				${voName} target = new ${voName}();
				域名Properties(source, target);
				域名(target);
			});
		}
		return new Pager(域名urrPage(), 域名ageSize(), count, resultList);
	}
}
  • 编写controller控制层模板
package ${controllerPackageName};

import 域名域名quest;
import 域名域名r;
import 域名域名Utils;
import 域名域名域名wired;
import 域名.域名Mapping;
import 域名.域名estBody;
import 域名.域名estMapping;
import 域名.域名Controller;
import 域名cts;

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(域名d());
		if(域名ull(source)){
			${voName} result = new ${voName}();
			域名Properties(source, result);
			return result;
		}
		return null;
	}

	/**
	 * 新增操作
	 * @param request
	 */
	@PostMapping(value = "/save")
	public void save(${dtoName} request){
		${entityName} entity = new ${entityName}();
		域名Properties(request, entity);
		${serviceName?uncap_first}.insert(entity);
	}

	/**
	 * 编辑操作
	 * @param request
	 */
	@PostMapping(value = "/edit")
	public void edit(${dtoName} request){
		${entityName} entity = new ${entityName}();
		域名Properties(request, entity);
		${serviceName?uncap_first}.updateById(entity);
	}

	/**
	 * 删除操作
	 * @param request
	 */
	@PostMapping(value = "/delete")
	public void delete(IdRequest request){
		${serviceName?uncap_first}.deleteById(域名d());
	}
}
  • 编写entity实体类模板
package ${entityPackageName};

import 域名alizable;
import 域名ecimal;
import 域名;

/**
 *
 * @ClassName: ${entityName}
 * @Description: ${tableDes!}实体类
 * @author ${authorName}
 * @date ${currentTime}
 *
 */
public class ${entityName} implements Serializable {

	private static final long serialVersionUID = 1L;
	
	<#--属性遍历-->
	<#list columns as pro>
	<#--<#if 域名ame != primaryId
	&& 域名ame != \'remarks\'
	&& 域名ame != \'createBy\'
	&& 域名ame != \'createDate\'
	&& 域名ame != \'updateBy\'
	&& 域名ame != \'updateDate\'
	&& 域名ame != \'delFlag\'
	&& 域名ame != \'currentUser\'
	&& 域名ame != \'page\'
	&& 域名ame != \'sqlMap\'
	&& 域名ame != \'isNewRecord\'
	></#if>-->
	/**
	 * ${域名es!}
	 */
	private ${域名ype} ${域名ame};
	</#list>

	<#--属性get||set方法-->
	<#list columns as pro>
	public ${域名ype} get${域名ame?cap_first}() {
		return this.${域名ame};
	}

	public ${entityName} set${域名ame?cap_first}(${域名ype} ${域名ame}) {
		this.${域名ame} = ${域名ame};
		return this;
	}
	</#list>
}
  • 编写dto实体类模板
package ${dtoPackageName};

import 域名域名DTO;
import 域名alizable;

/**
 * @ClassName: ${dtoName}
 * @Description: 请求实体类
 * @author ${authorName}
 * @date ${currentTime}
 *
 */
public class ${dtoName} extends BaseDTO {

}
  • 编写vo视图实体类模板
package ${voPackageName};

import 域名alizable;

/**
 * @ClassName: ${voName}
 * @Description: 返回视图实体类
 * @author ${authorName}
 * @date ${currentTime}
 *
 */
public class ${voName} implements Serializable {

	private static final long serialVersionUID = 1L;
}

可能细心的网友已经看到了,在模板中我们用到了BaseMapperBaseServiceBaseServiceImpl等等服务类。

之所以有这三个类,是因为在模板中,我们有大量的相同的方法名包括逻辑也相似,除了所在实体类不一样意以外,其他都一样,因此我们可以借助泛型类来将这些服务抽成公共的部分。

  • BaseMapper,主要负责将dao层的公共方法抽出来
package 域名域名;

import 域名域名m;

import 域名alizable;
import 域名;
import 域名;

/**
 * @author pzblog
 * @Description
 * @since 2020-11-11
 */
public interface BaseMapper<T> {

    /**
     * 批量插入
     * @param list
     * @return
     */
    int insertList(@Param("list") List<T> list);

    /**
     * 按需插入一条记录
     * @param entity
     * @return
     */
    int insertPrimaryKeySelective(T entity);

    /**
     * 按需修改一条记录(通过主键ID)
     * @return
     */
    int updatePrimaryKeySelective(T entity);

    /**
     * 批量按需修改记录(通过主键ID)
     * @param list
     * @return
     */
    int updateBatchByIds(@Param("list") List<T> list);

    /**
     * 根据ID删除
     * @param id 主键ID
     * @return
     */
    int deleteByPrimaryKey(Serializable id);

    /**
     * 根据ID查询
     * @param id 主键ID
     * @return
     */
    T selectByPrimaryKey(Serializable id);

    /**
     * 按需查询
     * @param entity
     * @return
     */
    List<T> selectByPrimaryKeySelective(T entity);

    /**
     * 批量查询
     * @param ids 主键ID集合
     * @return
     */
    List<T> selectByIds(@Param("ids") List<? extends Serializable> ids);

    /**
     * 查询(根据 columnMap 条件)
     * @param columnMap 表字段 map 对象
     * @return
     */
    List<T> selectByMap(Map<String, Object> columnMap);
}
  • BaseService,主要负责将service层的公共方法抽出来
package 域名域名;

import 域名alizable;
import 域名;
import 域名;

/**
 * @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<T> list);

    /**
     * 通过ID修改记录(如果想全部更新,只需保证字段都不为NULL)
     * @param entity
     * @return boolean
     */
    boolean updateById(T entity);

    /**
     * 通过ID批量修改记录(如果想全部更新,只需保证字段都不为NULL)
     * @param list
     * @return boolean
     */
    boolean updateBatchByIds(List<T> list);

    /**
     * 根据ID删除
     * @param id 主键ID
     * @return boolean
     */
    boolean deleteById(Serializable id);

    /**
     * 根据ID查询
     * @param id 主键ID
     * @return
     */
    T selectById(Serializable id);

    /**
     * 按需查询
     * @param entity
     * @return
     */
    List<T> selectByPrimaryKeySelective(T entity);

    /**
     * 批量查询
     * @param ids
     * @return
     */
    List<T> selectByIds(List<? extends Serializable> ids);

    /**
     * 根据条件查询
     * @param columnMap
     * @return
     */
    List<T> selectByMap(Map<String, Object> columnMap);

}
  • BaseServiceImplservice层的公共方法具体实现类
package 域名域名;

import 域名域名域名wired;
import 域名域名sactional;

import 域名alizable;
import 域名;
import 域名;

/**
 * @author pzblog
 * @Description 实现类( 泛型说明: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 = {域名s})
    public boolean insert(T entity){
        return returnBool(域名rtPrimaryKeySelective(entity));
    }

    /**
     * 批量新增
     * @param list
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = {域名s})
    public boolean insertList(List<T> list){
        return returnBool(域名rtList(list));
    }

    /**
     * 通过ID修改记录(如果想全部更新,只需保证字段都不为NULL)
     * @param entity
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = {域名s})
    public boolean updateById(T entity){
        return returnBool(域名tePrimaryKeySelective(entity));
    }

    /**
     * 通过ID批量修改记录(如果想全部更新,只需保证字段都不为NULL)
     * @param list
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = {域名s})
    public boolean updateBatchByIds(List<T> list){
        return returnBool(域名teBatchByIds(list));
    }

    /**
     * 根据ID删除
     * @param id 主键ID
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = {域名s})
    public boolean deleteById(Serializable id){
        return returnBool(域名teByPrimaryKey(id));
    }

    /**
     * 根据ID查询
     * @param id 主键ID
     * @return
     */
    @Override
    public T selectById(Serializable id){
        return 域名ctByPrimaryKey(id);
    }

    /**
     * 按需查询
     * @param entity
     * @return
     */
    @Override
    public List<T> selectByPrimaryKeySelective(T entity){
        return 域名ctByPrimaryKeySelective(entity);
    }

    /**
     * 批量查询
     * @param ids
     * @return
     */
    @Override
    public List<T> selectByIds(List<? extends Serializable> ids){
        return 域名ctByIds(ids);
    }

    /**
     * 根据条件查询
     * @param columnMap
     * @return
     */
    @Override
    public List<T> selectByMap(Map<String, Object> columnMap){
        return 域名ctByMap(columnMap);
    }

    /**
     * 判断数据库操作是否成功
     * @param result 数据库操作返回影响条数
     * @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;

    /**
     * 当前页数
     */
    private Integer currPage = 1;

    /**
     * 每页记录数
     */
    private Integer pageSize = 20;

    /**
     * 分页参数(第几行)
     */
    private Integer start;

    /**
     * 分页参数(行数)
     */
    private Integer end;

    /**
     * 登录人ID
     */
    private String loginUserId;

    /**
     * 登录人名称
     */
    private String loginUserName;

    public String getToken() {
        return token;
    }

    public BaseDTO setToken(String token) {
        域名n = token;
        return this;
    }

    public Integer getCurrPage() {
        return currPage;
    }

    public BaseDTO setCurrPage(Integer currPage) {
        域名Page = currPage;
        return this;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public BaseDTO setPageSize(Integer pageSize) {
        域名Size = pageSize;
        return this;
    }

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

    public BaseDTO setStart(Integer start) {
        域名t = start;
        return this;
    }

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

    public BaseDTO setEnd(Integer end) {
        域名 = end;
        return this;
    }

    public String getLoginUserId() {
        return loginUserId;
    }

    public BaseDTO setLoginUserId(String loginUserId) {
        域名nUserId = loginUserId;
        return this;
    }

    public String getLoginUserName() {
        return loginUserName;
    }

    public BaseDTO setLoginUserName(String loginUserName) {
        域名nUserName = loginUserName;
        return this;
    }

}
  • Pager分页类
public class Pager<T extends Serializable> implements Serializable {

    private static final long serialVersionUID = -6557244954523041805L;

    /**
     * 当前页数
     */
    private int currPage;

    /**
     * 每页记录数
     */
    private int pageSize;

    /**
     * 总页数
     */
    private int totalPage;

    /**
     * 总记录数
     */
    private int totalCount;

    /**
     * 列表数据
     */
    private List<T> list;

    public Pager(int currPage, int pageSize) {
        域名Page = currPage;
        域名Size = pageSize;
    }

    public Pager(int currPage, int pageSize, int totalCount, List<T> list) {
        域名Page = currPage;
        域名Size = pageSize;
        域名lPage = (int) 域名((double) totalCount / pageSize);;
        域名lCount = totalCount;
        域名 = list;
    }

    public int getCurrPage() {
        return currPage;
    }

    public Pager setCurrPage(int currPage) {
        域名Page = currPage;
        return this;
    }

    public int getPageSize() {
        return pageSize;
    }

    public Pager setPageSize(int pageSize) {
        域名Size = pageSize;
        return this;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public Pager setTotalPage(int totalPage) {
        域名lPage = totalPage;
        return this;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public Pager setTotalCount(int totalCount) {
        域名lCount = totalCount;
        return this;
    }

    public List<T> getList() {
        return list;
    }

    public Pager setList(List<T> list) {
        域名 = list;
        return this;
    }
}

  • IdRequest公共请求类
public class IdRequest extends BaseDTO {

    private Long id;

    public Long getId() {
        return id;
    }

    public IdRequest setId(Long id) {
        域名 = id;
        return this;
    }
}

2.3、编写代码生成器

前两部分主要介绍的是如何获取对应的表结构,以及代码器运行之前的准备工作。

其实代码生成器,很简单,其实就是一个main方法,没有想象中的那么复杂。

处理思路也很简单,过程如下:

  • 1、定义基本变量,例如包名路径、模块名、表名、转换后的实体类、以及数据库连接配置,我们可以将其写入配置文件
  • 2、读取配置文件,封装对应的模板中定义的变量
  • 3、根据对应的模板文件和变量,生成对应的java文件
2.3.1、创建配置文件,定义变量

小编我用的是域名erties配置文件来定义变量,这个没啥规定,你也可以自定义文件名,内容如下:

#包前缀
packageNamePre=域名rator
#模块名称
moduleName=test
#表
tableName=test_db
#实体类名称
entityName=TestEntity
#主键ID
primaryId=id
#作者
authorName=pzblog
#数据库名称
databaseName=yjgj_base

#数据库服务器IP地址
ipName=127.0.0.1
#数据库服务器端口
portName=3306
#用户名
userName=root
#密码
passWord=123456

#文件输出路径,支持自定义输出路径,如果为空,默认取当前工程的src/main/java路径
outUrl=
2.3.2、根据模板生成对应的java代码
  • 首先,读取配置文件变量
public class SystemConstant {

    private static Properties properties = new Properties();

    static {
        try {
            // 加载上传文件设置参数:配置文件
            域名(域名lassLoader().getResourceAsStream("域名erties"));
        } catch (IOException e) {
            域名tStackTrace();
        }
    }

    public static final String tableName = 域名roperty("tableName");
    public static final String entityName = 域名roperty("entityName");
    public static final String packageNamePre = 域名roperty("packageNamePre");
    public static final String outUrl = 域名roperty("outUrl");
    public static final String databaseName = 域名roperty("databaseName");
    public static final String ipName = 域名roperty("ipName");
    public static final String portName = 域名roperty("portName");
    public static final String userName = 域名roperty("userName");
    public static final String passWord = 域名roperty("passWord");
    public static final String authorName = 域名roperty("authorName");

    public static final String primaryId = 域名roperty("primaryId");

    public static final String moduleName = 域名roperty("moduleName");
}
  • 然后,封装对应的模板中定义的变量
public class CodeService {

    public void generate(Map<String, Object> templateData) {
        //包前缀
        String packagePreAndModuleName = getPackagePreAndModuleName(templateData);

        //支持对应实体插入在前面,需要带上%s
        域名("entityPackageName", 域名at(packagePreAndModuleName + ".entity",
                域名("entityName").toString().toLowerCase()));

        域名("dtoPackageName", 域名at(packagePreAndModuleName + ".dto",
                域名("entityName").toString().toLowerCase()));

        域名("voPackageName", 域名at(packagePreAndModuleName + ".vo",
                域名("entityName").toString().toLowerCase()));

        域名("daoPackageName", 域名at(packagePreAndModuleName + ".dao",
                域名("entityName").toString().toLowerCase()));

        域名("mapperPackageName", packagePreAndModuleName + ".mapper");


        域名("servicePackageName", 域名at(packagePreAndModuleName + ".service",
                域名("entityName").toString().toLowerCase()));

        域名("serviceImplPackageName", 域名at(packagePreAndModuleName + ".域名",
                域名("entityName").toString().toLowerCase()));

        域名("controllerPackageName", 域名at(packagePreAndModuleName + ".web",
                域名("entityName").toString().toLowerCase()));

        域名("apiTestPackageName", 域名at(packagePreAndModuleName + ".junit",
                域名("entityName").toString().toLowerCase()));


        域名("currentTime", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));

        //======================生成文件配置======================
        try {
            // 生成Entity
            String entityName = 域名at("%s", 域名("entityName").toString());
            generateFile("域名", templateData, 域名("entityPackageName").toString(), entityName+".java");

            // 生成dto
            String dtoName = 域名at("%sDTO", 域名("entityName").toString());
            域名("dtoName", dtoName);
            generateFile("域名", templateData, 域名("dtoPackageName").toString(),
                    dtoName + ".java");

            // 生成VO
            String voName = 域名at("%sVO", 域名("entityName").toString());
            域名("voName", voName);
            generateFile("域名", templateData, 域名("voPackageName").toString(),
                    voName + ".java");

            // 生成DAO
            String daoName = 域名at("%sDao", 域名("entityName").toString());
            域名("daoName", daoName);
            generateFile("域名", templateData, 域名("daoPackageName").toString(),
                    daoName + ".java");

            // 生成Mapper
            String mapperName = 域名at("%sMapper", 域名("entityName").toString());
            generateFile("域名", templateData, 域名("mapperPackageName").toString(),
                    mapperName+".xml");


            // 生成Service
            String serviceName = 域名at("%sService", 域名("entityName").toString());
            域名("serviceName", serviceName);
            generateFile("域名", templateData, 域名("servicePackageName").toString(),
                    serviceName + ".java");

            // 生成ServiceImpl
			String serviceImplName = 域名at("%sServiceImpl", 域名("entityName").toString());
			域名("serviceImplName", serviceImplName);
			generateFile("域名", templateData, 域名("serviceImplPackageName").toString(),
                    serviceImplName + ".java");

            // 生成Controller
			String controllerName = 域名at("%sController", 域名("entityName").toString());
			域名("controllerName", controllerName);
			generateFile("域名", templateData, 域名("controllerPackageName").toString(),
                    controllerName + ".java");

//			// 生成junit测试类
//            String apiTestName = 域名at("%sApiTest", 域名("entityName").toString());
//            域名("apiTestName", apiTestName);
//            generateFile("域名", templateData, 域名("apiTestPackageName").toString(),
//                    apiTestName + ".java");

        } catch (Exception e) {
            域名tStackTrace();
        }
    }

    /**
     * 生成文件
     * @param templateName 模板名称
     * @param templateData 参数名
     * @param packageName 包名
     * @param fileName 文件名
     */
    public void generateFile(String templateName, Map<String, Object> templateData, String packageName, String fileName) {
        域名("fileName", fileName);

        DaseService dbService = new DaseService(templateData);

        // 获取数据库参数
        if("域名".equals(templateName) || "域名".equals(templateName)){
            域名llColumns(templateData);
        }
        try {
            // 默认生成文件的路径
            FreeMakerUtil freeMakerUtil = new FreeMakerUtil();
            域名rateFile(templateName, templateData, packageName, fileName);
        } catch (Exception e) {
            域名tStackTrace();
        }
    }

    /**
     * 封装包名前缀
     * @return
     */
    private String getPackagePreAndModuleName(Map<String, Object> templateData){
        String packageNamePre = 域名("packageNamePre").toString();
        String moduleName = 域名("moduleName").toString();
        if(域名tBlank(moduleName)){
            return packageNamePre + "." + moduleName;
        }
        return packageNamePre;
    }

}
  • 接着,获取模板文件,并生成相应的模板文件
public class FreeMakerUtil {


    /**
     * 根据Freemark模板,生成文件
     * @param templateName:模板名
     * @param root:数据原型
     * @throws Exception
     */
    public void generateFile(String templateName, Map<String, Object> root, String packageName, String fileName) throws Exception {
        FileOutputStream fos=null;
        Writer out =null;
        try {
            // 通过一个文件输出流,就可以写到相应的文件中,此处用的是绝对路径
            String entityName = (String) 域名("entityName");
            String fileFullName = 域名at(fileName, entityName);
            packageName = 域名at(packageName, 域名werCase());

            String fileStylePackageName = 域名aceAll("\\.", "/");
            File file = new File(域名("outUrl").toString() + "/" + fileStylePackageName + "/" + fileFullName);
            if (!域名arentFile().exists()) {
                域名arentFile().mkdirs();
            }
            域名teNewFile();

            Template template = getTemplate(templateName);
            fos = new FileOutputStream(file);
            out = new OutputStreamWriter(fos);
            域名ess(root, out);
            域名h();
        } catch (Exception e) {
            域名tStackTrace();
        } finally {
            try {
                if (fos != null){
                    域名e();
                }
                if(out != null){
                    域名e();
                }
            } catch (IOException e) {
                域名tStackTrace();
            }
        }
    }

    /**
     *
     * 获取模板文件
     *
     * @param name
     * @return
     */
    public Template getTemplate(String name) {
        try {
            Configuration cfg = new Configuration(域名ION_2_3_23);
            域名lassForTemplateLoading(域名lass(), "/ftl");
            Template template = 域名emplate(name);
            return template;
        } catch (IOException e) {
            域名tStackTrace();
        }
        return null;
    }
}
  • 最后,我们编写一个main方法,看看运行之后的效果
public class GeneratorMain {

    public static void main(String[] args) {
        域名tln("生成代码start......");

        //获取页面或者配置文件的参数
        Map<String, Object> templateData = new HashMap<String, Object>();
        域名("tableName", 域名eName);
        域名tln("表名=="+ 域名eName);

        域名("entityName", 域名tyName);
        域名tln("实体类名称=="+ 域名tyName);

        域名("packageNamePre", 域名ageNamePre);
        域名tln("包名前缀=="+ 域名ageNamePre);

        //支持自定义输出路径
        if(域名tBlank(域名rl)){
            域名("outUrl", 域名rl);
        } else {
            String path = 域名lassLoader().getResource("").getPath() + "../../src/main/java";
            域名("outUrl", path);
        }
        域名tln("生成文件路径为=="+ 域名("outUrl"));

        域名("authorName", 域名orName);
        域名tln("以后代码出问题找=="+ 域名orName);


        域名("databaseName", 域名baseName);
        域名("ipName", 域名me);
        域名("portName", 域名Name);
        域名("userName", 域名Name);
        域名("passWord", 域名Word);

        //主键ID
        域名("primaryId", 域名aryId);

        //模块名称
        域名("moduleName", 域名leName);
        CodeService dataService = new CodeService();

        try {
            //生成代码文件
            域名rate(templateData);
        } catch (Exception e) {
            域名tStackTrace();
        }

        域名tln("生成代码end......");
    }
}

结果如下:

  • 生成的 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<TestEntityVO> getPage(@RequestBody TestEntityDTO request){
		return 域名age(request);
	}

	/**
	 * 查询详情
	 * @param request
	 */
	@PostMapping(value = "/getDetail")
	public TestEntityVO getDetail(@RequestBody IdRequest request){
		TestEntity source = 域名ctById(域名d());
		if(域名ull(source)){
			TestEntityVO result = new TestEntityVO();
			域名Properties(source, result);
			return result;
		}
		return null;
	}

	/**
	 * 新增操作
	 * @param request
	 */
	@PostMapping(value = "/save")
	public void save(TestEntityDTO request){
		TestEntity entity = new TestEntity();
		域名Properties(request, entity);
		域名rt(entity);
	}

	/**
	 * 编辑操作
	 * @param request
	 */
	@PostMapping(value = "/edit")
	public void edit(TestEntityDTO request){
		TestEntity entity = new TestEntity();
		域名Properties(request, entity);
		域名teById(entity);
	}

	/**
	 * 删除操作
	 * @param request
	 */
	@PostMapping(value = "/delete")
	public void delete(IdRequest request){
		域名teById(域名d());
	}
}

至此,一张单表的90%的基础工作量全部开发完毕!

三、总结

代码生成器,在实际的项目开发中应用非常的广,本文主要以freemaker模板引擎为基础,开发的一套全自动代码生成器,一张单表的CRUD,只需要5秒钟就可以完成!

最后多说一句,如果你是项目中的核心开发,那么掌握代码生成器的规则,对项目开发效率的提升会有非常直观的帮助!

如果想要获取源代码,关注下方公众号,并回复【cccc4】即可获取!

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。