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

博客 动态
0 341
羽尘
羽尘 2022-02-03 21:54:53
悬赏:0 积分 收藏

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

如果你想针对当前的项目开发一套定制化代码生成器,本文将给你答案!

一、简介

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

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

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

于是就想着,能不能通过代码生成器一键搞定全部的 crud?

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

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

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

二、实现思路

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

实现思路如下:

  • 第一步:获取表字段名称、类型、表注释等信息
  • 第二步:基于 freemarker 模板引擎,编写相应的模板
  • 第三步:根据对应的模板,生成相应的 java 代码

2.1、获取表结构

首先我们创建一张test_db表,脚本如下:

CREATE TABLE test_db (  id bigint(20) unsigned NOT NULL COMMENT '主键ID',  name varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',  is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',  create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  PRIMARY KEY (id),  KEY idx_create_time (create_time) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='测试表';

表创建完成之后,基于test_db表,我们查询对应的表结果字段名称、类型、备注信息,这些信息收集将用于后续进行代码生成器所使用

# 获取对应表结构SELECT column_name, data_type, column_comment FROM information_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模板,涵盖新增、修改、删除、查询等信息
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="${daoPackageName}.${daoName}">	<!--BaseResultMap-->	<resultMap id="BaseResultMap" type="${entityPackageName}.${entityName}">        <#list columns as pro>            <#if pro.proName == primaryId>				<id column="${primaryId}" property="${primaryId}" jdbcType="${pro.fieldType}"/>            <#else>				<result column="${pro.fieldName}" property="${pro.proName}" jdbcType="${pro.fieldType}"/>            </#if>        </#list>	</resultMap>	<!--Base_Column_List-->	<sql id="Base_Column_List">        <#list columns as pro>            <#if pro_index == 0>${pro.fieldName}<#else>,${pro.fieldName}</#if>        </#list>	</sql>	<!--批量插入-->	<insert id="insertList" parameterType="java.util.List">		insert into ${tableName} (        <#list columns as pro>            <#if pro_index == 0>${pro.fieldName},<#elseif pro_index == 1>${pro.fieldName}<#else>,${pro.fieldName}</#if>        </#list>		)		values		<foreach collection ="list" item="obj" separator =",">			<trim prefix=" (" suffix=")" suffixOverrides=",">                <#list columns as pro>                    ${r"#{obj." + pro.proName + r"}"},                </#list>			</trim>		</foreach >	</insert>	<!--按需新增-->	<insert id="insertPrimaryKeySelective" parameterType="${entityPackageName}.${entityName}">		insert into ${tableName}		<trim prefix="(" suffix=")" suffixOverrides=",">            <#list columns as pro>				<if test="${pro.proName} != null">                    ${pro.fieldName},				</if>            </#list>		</trim>		<trim prefix="values (" suffix=")" suffixOverrides=",">            <#list columns as pro>				<if test="${pro.proName} != null">                    ${r"#{" + pro.proName + r",jdbcType=" + pro.fieldType +r"}"},				</if>            </#list>		</trim>	</insert>	<!-- 按需修改-->	<update id="updatePrimaryKeySelective" parameterType="${entityPackageName}.${entityName}">		update ${tableName}		<set>            <#list columns as pro>                <#if pro.fieldName != primaryId && pro.fieldName != primaryId>					<if test="${pro.proName} != null">                        ${pro.fieldName} = ${r"#{" + pro.proName + r",jdbcType=" + pro.fieldType +r"}"},					</if>                </#if>            </#list>		</set>		where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"}	</update>	<!-- 按需批量修改-->	<update id="updateBatchByIds" parameterType="java.util.List">		update ${tableName}		<trim prefix="set" suffixOverrides=",">            <#list columns as pro>                <#if pro.fieldName != primaryId && pro.fieldName != primaryId>					<trim prefix="${pro.fieldName}=case" suffix="end,">						<foreach collection="list" item="obj" index="index">							<if test="obj.${pro.proName} != null">								when id = ${r"#{" + "obj.id" + r"}"}								then  ${r"#{obj." + pro.proName + r",jdbcType=" + pro.fieldType +r"}"}							</if>						</foreach>					</trim>                </#if>            </#list>		</trim>		where		<foreach collection="list" separator="or" item="obj" index="index" >			id = ${r"#{" + "obj.id" + r"}"}		</foreach>	</update>	<!-- 删除-->	<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">		delete from ${tableName}		where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"}	</delete>	<!-- 查询详情 -->	<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long">		select		<include refid="Base_Column_List"/>		from ${tableName}		where ${primaryId} = ${r"#{" + "${primaryId}" + r",jdbcType=BIGINT}"}	</select>	<!-- 按需查询 -->	<select id="selectByPrimaryKeySelective" resultMap="BaseResultMap" parameterType="${entityPackageName}.${entityName}">		select		<include refid="Base_Column_List"/>		from ${tableName}	</select>	<!-- 批量查询-->	<select id="selectByIds" resultMap="BaseResultMap" parameterType="java.util.List">		select		<include refid="Base_Column_List"/>		from ${tableName}		<where>			<if test="ids != null">				and ${primaryId} in				<foreach item="item" index="index" collection="ids" open="(" separator="," close=")">                    ${r"#{" + "item" + r"}"}				</foreach>			</if>		</where>	</select>	<!-- 根据条件查询 -->	<select id="selectByMap" resultMap="BaseResultMap" parameterType="java.util.Map">		select		<include refid="Base_Column_List"/>		from ${tableName}	</select>	<!-- 查询${entityName}总和 -->	<select id="countPage" resultType="int" parameterType="${dtoPackageName}.${dtoName}">		select count(${primaryId})		from ${tableName}	</select>	<!-- 查询${entityName}列表 -->	<select id="selectPage" resultMap="BaseResultMap" parameterType="${dtoPackageName}.${dtoName}">		select		<include refid="Base_Column_List"/>		from ${tableName}		limit ${r"#{" + "start,jdbcType=INTEGER" + r"}"},${r"#{" + "end,jdbcType=INTEGER" + r"}"}	</select></mapper>
  • 编写dao数据访问模板
package ${daoPackageName};import com.example.generator.core.BaseMapper;import java.util.List;import ${entityPackageName}.${entityName};import ${dtoPackageName}.${dtoName};/**** @ClassName: ${daoName}* @Description: 数据访问接口* @author ${authorName}* @date ${currentTime}**/public interface ${daoName} extends BaseMapper<${entityName}>{	int countPage(${dtoName} ${dtoName?uncap_first});	List<${entityName}> selectPage(${dtoName} ${dtoName?uncap_first});}
  • 编写service服务接口模板
package ${servicePackageName};import com.example.generator.core.BaseService;import com.example.generator.common.Pager;import ${voPackageName}.${voName};import ${dtoPackageName}.${dtoName};import ${entityPackageName}.${entityName};/** * * @ClassName: ${serviceName} * @Description: ${entityName}业务访问接口 * @author ${authorName} * @date ${currentTime} * */public interface ${serviceName} extends BaseService<${entityName}> {	/**	 * 分页列表查询	 * @param request	 */	Pager<${voName}> getPage(${dtoName} request);}
  • 编写serviceImpl服务实现类模板
package ${serviceImplPackageName};import com.example.generator.common.Pager;import com.example.generator.core.BaseServiceImpl;import com.example.generator.test.service.TestEntityService;import org.springframework.beans.BeanUtils;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.ArrayList;import java.util.List;import ${daoPackageName}.${daoName};import ${entityPackageName}.${entityName};import ${dtoPackageName}.${dtoName};import ${voPackageName}.${voName};@Servicepublic class ${serviceImplName} extends BaseServiceImpl<${daoName}, ${entityName}> implements ${serviceName} {	private static final Logger log = LoggerFactory.getLogger(${serviceImplName}.class);	/**	 * 分页列表查询	 * @param request	 */	public Pager<${voName}> getPage(${dtoName} request) {		List<${voName}> resultList = new ArrayList();		int count = super.baseMapper.countPage(request);		List<${entityName}> dbList = count > 0 ? super.baseMapper.selectPage(request) : new ArrayList<>();		if(!CollectionUtils.isEmpty(dbList)){			dbList.forEach(source->{				${voName} target = new ${voName}();				BeanUtils.copyProperties(source, target);				resultList.add(target);			});		}		return new Pager(request.getCurrPage(), request.getPageSize(), count, resultList);	}}
  • 编写controller控制层模板
package ${controllerPackageName};import com.example.generator.common.IdRequest;import com.example.generator.common.Pager;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Objects;import ${servicePackageName}.${serviceName};import ${entityPackageName}.${entityName};import ${dtoPackageName}.${dtoName};import ${voPackageName}.${voName};/** * * @ClassName: ${controllerName} * @Description: 外部访问接口 * @author ${authorName} * @date ${currentTime} * */@RestController@RequestMapping("/${entityName?uncap_first}")public class ${controllerName} {	@Autowired	private ${serviceName} ${serviceName?uncap_first};	/**	 * 分页列表查询	 * @param request	 */	@PostMapping(value = "/getPage")	public Pager<${voName}> getPage(@RequestBody ${dtoName} request){		return ${serviceName?uncap_first}.getPage(request);	}	/**	 * 查询详情	 * @param request	 */	@PostMapping(value = "/getDetail")	public ${voName} getDetail(@RequestBody IdRequest request){		${entityName} source = ${serviceName?uncap_first}.selectById(request.getId());		if(Objects.nonNull(source)){			${voName} result = new ${voName}();			BeanUtils.copyProperties(source, result);			return result;		}		return null;	}	/**	 * 新增操作	 * @param request	 */	@PostMapping(value = "/save")	public void save(${dtoName} request){		${entityName} entity = new ${entityName}();		BeanUtils.copyProperties(request, entity);		${serviceName?uncap_first}.insert(entity);	}	/**	 * 编辑操作	 * @param request	 */	@PostMapping(value = "/edit")	public void edit(${dtoName} request){		${entityName} entity = new ${entityName}();		BeanUtils.copyProperties(request, entity);		${serviceName?uncap_first}.updateById(entity);	}	/**	 * 删除操作	 * @param request	 */	@PostMapping(value = "/delete")	public void delete(IdRequest request){		${serviceName?uncap_first}.deleteById(request.getId());	}}
  • 编写entity实体类模板
package ${entityPackageName};import java.io.Serializable;import java.math.BigDecimal;import java.util.Date;/** * * @ClassName: ${entityName} * @Description: ${tableDes!}实体类 * @author ${authorName} * @date ${currentTime} * */public class ${entityName} implements Serializable {	private static final long serialVersionUID = 1L;		<#--属性遍历-->	<#list columns as pro>	<#--<#if pro.proName != primaryId	&& pro.proName != 'remarks'	&& pro.proName != 'createBy'	&& pro.proName != 'createDate'	&& pro.proName != 'updateBy'	&& pro.proName != 'updateDate'	&& pro.proName != 'delFlag'	&& pro.proName != 'currentUser'	&& pro.proName != 'page'	&& pro.proName != 'sqlMap'	&& pro.proName != 'isNewRecord'	></#if>-->	/**	 * ${pro.proDes!}	 */	private ${pro.proType} ${pro.proName};	</#list>	<#--属性get||set方法-->	<#list columns as pro>	public ${pro.proType} get${pro.proName?cap_first}() {		return this.${pro.proName};	}	public ${entityName} set${pro.proName?cap_first}(${pro.proType} ${pro.proName}) {		this.${pro.proName} = ${pro.proName};		return this;	}	</#list>}
  • 编写dto实体类模板
package ${dtoPackageName};import com.example.generator.core.BaseDTO;import java.io.Serializable;/** * @ClassName: ${dtoName} * @Description: 请求实体类 * @author ${authorName} * @date ${currentTime} * */public class ${dtoName} extends BaseDTO {}
  • 编写vo视图实体类模板
package ${voPackageName};import java.io.Serializable;/** * @ClassName: ${voName} * @Description: 返回视图实体类 * @author ${authorName} * @date ${currentTime} * */public class ${voName} implements Serializable {	private static final long serialVersionUID = 1L;}

可能细心的网友已经看到了,在模板中我们用到了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<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 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<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 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 实现类( 泛型说明: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));    }    /**     * 根据ID删除     * @param id 主键ID     * @return boolean     */    @Override    @Transactional(rollbackFor = {Exception.class})    public boolean deleteById(Serializable id){        return returnBool(baseMapper.deleteByPrimaryKey(id));    }    /**     * 根据ID查询     * @param id 主键ID     * @return     */    @Override    public T selectById(Serializable id){        return baseMapper.selectByPrimaryKey(id);    }    /**     * 按需查询     * @param entity     * @return     */    @Override    public List<T> selectByPrimaryKeySelective(T entity){        return baseMapper.selectByPrimaryKeySelective(entity);    }    /**     * 批量查询     * @param ids     * @return     */    @Override    public List<T> selectByIds(List<? extends Serializable> ids){        return baseMapper.selectByIds(ids);    }    /**     * 根据条件查询     * @param columnMap     * @return     */    @Override    public List<T> selectByMap(Map<String, Object> columnMap){        return baseMapper.selectByMap(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) {        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;    /**     * 当前页数     */    private int currPage;    /**     * 每页记录数     */    private int pageSize;    /**     * 总页数     */    private int totalPage;    /**     * 总记录数     */    private int totalCount;    /**     * 列表数据     */    private List<T> list;    public Pager(int currPage, int pageSize) {        this.currPage = currPage;        this.pageSize = pageSize;    }    public Pager(int currPage, int pageSize, int totalCount, List<T> 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<T> getList() {        return list;    }    public Pager setList(List<T> 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、编写代码生成器

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

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

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

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

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

#包前缀packageNamePre=com.example.generator#模块名称moduleName=test#表tableName=test_db#实体类名称entityName=TestEntity#主键IDprimaryId=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 {            // 加载上传文件设置参数:配置文件            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<String, Object> 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 参数名     * @param packageName 包名     * @param fileName 文件名     */    public void generateFile(String templateName, Map<String, Object> templateData, String packageName, String fileName) {        templateData.put("fileName", fileName);        DaseService dbService = new DaseService(templateData);        // 获取数据库参数        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<String, Object> 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 {    /**     * 根据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) 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......");        //获取页面或者配置文件的参数        Map<String, Object> templateData = new HashMap<String, Object>();        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......");    }}

结果如下:

  • 生成的 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 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%的基础工作量全部开发完毕!

三、总结

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

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

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


作者:程序员志哥
出处:www.pzblog.cn
资源:微信搜【Java极客技术】关注我,回复 【cccc】有我准备的一线程序必备计算机书籍、大厂面试资料和免费电子书。 一共24G的资料,希望可以帮助大家提升技术和能力。

posted @ 2022-02-03 21:03 程序员志哥 阅读(21) 评论(0) 编辑 收藏 举报
回帖
    羽尘

    羽尘 (王者 段位)

    2335 积分 (2)粉丝 (11)源码

     

    温馨提示

    亦奇源码

    最新会员