手把手教你拥有自己的代码生成器

哈哈,猿设计终于讲完了,接下来的一段时间,工厂君打算和大家一起来实现我们之间的设计——构建一个自己的电商系统来玩耍。嗯,这个可是一个大工程,代码量很大,业务也比较复杂,要搞就好好搞,代码方面还是需要一些规范的。

在这个背景下,工厂君为了解决代码的编写速度,也差点被逼上梁山——一个人的力量实在太有限了。工厂君灵机一动,决定搞一个适合项目开发的利器出来——pz-cg。在它的帮帮助下,工厂君节约了开发时间,大大地提高了工作效率。

其实对于新手而言,过度的依赖这类工具,深受其害的事情是大概率事件,如果你是一个新手,希望你在以后的学习中,尽量的去手写代码。虽然会吃力一些,遇到各种稀奇古怪的问题,但这是你的猿人生涯中,必须去面对和解决的事情。言尽于此,不再絮叨。

其实代码生成器有很多,网络上也有各种各样的版本,自然是各有各的好处,也各有各的弊端,今天工厂君会带着大家去造轮子——造一个符合大多数公司编码规范,也能够支持你快速修改的代码生成轮子。造轮子其实也是快速提升你技术实力的一种方式,你经常吐槽CRUD没技术含量,那么你就写个制造CRUD的代码机器出来如何?这个过程也是能提高你的技术实力的,准备好你的收藏,今天的东西,以后你大概率用得上,能够帮你解决实际问题。

既然要搞一个轮子,我们还是希望这个轮子相对通用,不但可以支持springMVC、Spring、Mybatis,在SpringBoot,SpringCloud的框架下依然可用才是硬道理_

既然是代码生成器,那么我们不妨来想一想,我们要生成什么样的代码?什么样的代码是需要我们去生成的?我们要搞的代码生成的本质是什么?关于这个答案,我们需要从一些需要去完成的功能中,管中窥豹一番。就以我们后续会讲到的品牌管理为例吧。


上图的功能就是一个较为典型的管理功能,像这一类功能,都有一些共同的特点——新增、修改、删除、列表查询(包括分页功能)。这些功能是相对固定的,代码的编写方式也相对有一定的痕迹可以寻觅。我们先一起来看一看后端的代码。

一般来说,在使用SpringMVC、Spring、Mybatis框架,进行开发的方式,像上述这样一个比较基本的页面,对应的后端代码,会分为Controller、service、dao三个层面。Controller层负责页面数据的封装、service层负责组织业务逻辑,dao层负责持久化数据。当然,如果你要分得细一点,会加入一个manager层面,用于组织数据持久层的代码,将业务逻辑交由service进行管控也是常见的做法。考虑到这个功能比较简单,我们并没有使用manager层,稍微简单一些,我们先看代码。

/*** Copyright(c) 2004-2020 pangzi*com.pz.basic.mall.controller.sys.MallBrandController.java*/
package com.pz.basic.mall.controller.sys;import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.dmain.base.enums.DataActiveStatusEnum;
import com.pz.basic.mall.domain.base.enums.DataStatusEnum;
import com.pz.basic.mall.domain.sys.MallBrand;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;
import com.pz.basic.mall.service.sys.MallBrandService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/**** @author pangzi* @date 2020-06-22 20:47:27***/
@RestController
@RequestMapping("/brandManage")
public class MallBrandController {private MallBrandService mallBrandService;public void setMallBrandService(MallBrandService mallBrandService) {this.mallBrandService =mallBrandService;}/*** 新增品牌* @param mallBrand* @return*/@RequestMapping("/addMallBrand")public Result<MallBrand>  addMallBrand(@RequestBody MallBrandmallBrand){try{return   mallBrandService.addMallBrand(mallBrand);}catch(Exception e){e.printStackTrace();return new Result(false);}}/*** 修改品牌* @param mallBrand* @return*/@RequestMapping("/updateMallBrand")public Result updateMallBrand(@RequestBodyMallBrand mallBrand){try{return mallBrandService.updateMallBrandById(mallBrand);}catch(Exception e){e.printStackTrace();return new Result(false);}}/*** 删除品牌* @param mallBrand* @return*/@RequestMapping("/deleteMallBrand")public Result deleteMallBrand(@RequestBodyMallBrand mallBrand){try{return mallBrandService.deleteMallBrandById(mallBrand);}catch(Exception e){e.printStackTrace();return new Result(false);}}/*** 分页返回品牌列表* @param queryMallBrand* @return*/@RequestMapping("/findByPage")public Result<List<MallBrand>> findByPage(@RequestBody  QueryMallBrand queryMallBrand){returnmallBrandService.getMallBrandsByPage(queryMallBrand);}}
/*** Copyright(c) 2004-2020 pangzi*com.pz.basic.mall.service.sys.MallBrandService.java*/
package com.pz.basic.mall.service.sys;
import java.util.List;import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;import com.pz.basic.mall.domain.sys.MallBrand;
import java.util.Map;
import java.io.Serializable;/*** service层,组装外部接口 和 本地业务,为本业务 或者其他业务提供服务,统一返回Result* 通过Result.isSuccess判断调用是否成功* 此类中新增业务接口设计(接口命令,入参数据,返回值)要 能尽量完整的表达业务 含义* @author pangzi* @date 2020-06-26 11:20:40*/
public interface MallBrandService {/*** 新增 mallBrand* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getModel()得到新增mallBrand* @param mallBrand* @return*/public Result<MallBrand> addMallBrand(MallBrand mallBrand) ;/*** 按照主键id更新mallBrand,请重新newMallBrand 的更新对象,设置要更新的字段* 返回result,通过result.isSuccess()判断更新是否成功* @param id* @param mallBrand* @return*/public Result updateMallBrandById(MallBrandmallBrand);/*** 按照主键id 删除 记录* 返回result,通过result.isSuccess()判断删除是否成功* @param id* @return*/public Result deleteMallBrandById(MallBrandmallBrand);/*** 查询列表,此接口不包含分页查询* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getModel()得到列表信息* @param queryMallBrand* @return*/public Result<List<MallBrand>> getMallBrandsByQuery(QueryMallBrand queryMallBrand);/*** 通过主键id查询MallBrand* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getModel()得到查询的单条mallBrand信息* @param id* @return*/public Result<MallBrand> getMallBrandById(long id);/*** 查询列表,包含分页查询* 查询分页信息,请设置* QueryMallBrand.setIndex(设置当前页数)* QueryMallBrand.setPageSize(设置当前页面数据行数)* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getTotal()返回结果总数* 通过result.getModel()得到查询的单页列表信息* @param queryMallBrand* @return*/public Result<List<MallBrand>> getMallBrandsByPage(QueryMallBrand queryMallBrand);/*** 查询总数* @param queryMallBrand* @return*/public Result<Long>count(QueryMallBrand queryMallBrand);}

/*** Copyright(c) 2004-2020 pangzi*com.pz.basic.mall.service.sys.impl.MallBrandService.java*/
package com.pz.basic.mall.service.sys.impl;
import com.pz.basic.mall.dao.sys.MallBrandDao;import java.util.ArrayList;
import java.util.List;import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.base.enums.DataActiveStatusEnum;
import com.pz.basic.mall.domain.base.enums.DataStatusEnum;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;
import com.pz.basic.mall.service.sys.MallBrandService;import com.pz.basic.mall.domain.sys.MallBrand;/**** @author pangzi* @date 2020-06-26 11:25:00*/
public class MallBrandServiceImpl implements MallBrandService {private MallBrandDao mallBrandDao;public void setMallBrandDao (MallBrandDaomallBrandDao) {this.mallBrandDao = mallBrandDao;}public Result<MallBrand>addMallBrand(MallBrand mallBrand) {Result<MallBrand>result = new Result<MallBrand>();try {QueryMallBrand query= new QueryMallBrand();query.setBrandName(mallBrand.getBrandName());long count =mallBrandDao.countByQuery(query);if(count>0){result.setSuccess(false);result.setMessage("品牌名已存在");returnresult;}mallBrand.setStatus(DataStatusEnum.STATUS_ENABLE.getStatusValue());mallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());mallBrandDao.insertMallBrand(mallBrand);result.addDefaultModel(mallBrand);} catch(Exception e) {result.setSuccess(false);}return result;}public Result updateMallBrandById(MallBrandmallBrand) {Result result = new Result();try {int count=mallBrandDao.updateMallBrandByIdModified(mallBrand);if(count>0){result.setSuccess(true);}} catch(Exception e) {result.setSuccess(false);}return result;}public Result deleteMallBrandById(MallBrandmallBrand) {Result result = new Result();try {int count=0;MallBrandmodifiedMallBrand = new MallBrand();modifiedMallBrand.setId(mallBrand.getId());modifiedMallBrand.setActive(DataActiveStatusEnum.STATUS_DELETED.getStatusValue());count=mallBrandDao.updateMallBrandByIdModified(modifiedMallBrand);if(count>0){result.setSuccess(true);}} catch(Exception e) {result.setSuccess(false);}return result;}       public Result<List<MallBrand>>getMallBrandsByQuery(QueryMallBrand queryMallBrand) {Result<List<MallBrand>>result = new Result<List<MallBrand>>();try {queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());result.addDefaultModel("MallBrands",mallBrandDao.selectMallBrandByQuery(queryMallBrand));} catch(Exception e) {result.setSuccess(false);}return result;  }public Result<MallBrand>getMallBrandById(long id) {Result<MallBrand>result = new Result<MallBrand>();try {          result.addDefaultModel("MallBrand",mallBrandDao.selectMallBrandById(id));} catch(Exception e) {result.setSuccess(false);}return result;  }public Result<List<MallBrand>>getMallBrandsByPage(QueryMallBrand queryMallBrand) {Result<List<MallBrand>>result = new Result<List<MallBrand>>();queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());long totalItem =mallBrandDao.countByQuery(queryMallBrand);queryMallBrand.setTotalItem(totalItem);queryMallBrand.repaginate();if (totalItem > 0) {result.addDefaultModel(mallBrandDao.selectMallBrandByPage(queryMallBrand));} else {result.addDefaultModel(newArrayList<MallBrand>());}result.setTotalItem(totalItem);result.setPageSize(queryMallBrand.getPageSize());result.setPage(queryMallBrand.getPage());return result;}public Result<Long>count(QueryMallBrand queryMallBrand) {Result<Long> result =new Result<Long>();queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());try { result.addDefaultModel(mallBrandDao.countByQuery(queryMallBrand));} catch(Exception e) {result.setSuccess(false);}return result;  }}

/*** Copyright(c) 2004-2020 pangzi* com.pz.basic.mall.dao.sys.MallBrandDao.java*/
package com.pz.basic.mall.dao.sys;
import java.util.List;
import com.pz.basic.mall.domain.sys.MallBrand;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;import java.util.Map;
import java.io.Serializable;/**** @author pangzi* @date 2020-06-26 10:56:01*/
public interfaceMallBrandDao {/*** 根据条件查询总数* @param QueryMallBrand query* @return*/long countByQuery(QueryMallBrand query);/*** 根据条件删除记录* @param MallBrandQuery query* @return*/int deleteMallBrandByQuery(QueryMallBrandquery);/*** 根据ID删除记录* @param id* @return*/int deleteMallBrandById(long id);/*** 新增记录* @param MallBrand record* @return*/long insertMallBrand(MallBrand record);/*** 新增记录 注意:有值的记录才新增* @param MallBrand record* @return*/long insertMallBrandModified(MallBrandrecord);/*** 根据查询条件返回列表* @param QueryMallBrand query* @return*/List<MallBrand>selectMallBrandByQuery(QueryMallBrand query);/*** 根据查询条件返回列表* @param QueryMallBrand query* @return*/List<MallBrand> selectMallBrandByPage(QueryMallBrandquery);/*** 根据ID查询对象* @param Long id* @return*/MallBrand selectMallBrandById(long id);/*** 根据id修改记录 注意:有值的字段才更新* @param MallBrand record* @return*/int updateMallBrandByIdModified(MallBrandrecord);}

Mapper文件:

<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPEmapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mappernamespace="com.pz.basic.mall.dao.sys.MallBrandDao"><resultMap id="ResultMap"type="MallBrand"><idproperty="id" column="id"/><idproperty="brandName" column="brand_name"/><idproperty="logo" column="logo"/><idproperty="firstChar" column="first_char"/><idproperty="status" column="status"/><idproperty="active" column="active"/><idproperty="createUser" column="create_user"/><idproperty="modifyUser" column="modify_user"/><idproperty="created" column="created"/><idproperty="modified" column="modified"/></resultMap><sql id="ALL_TABLE_COLOUM">   id,brand_name,logo,first_char,status,active,create_user,modify_user,created,modified</sql><sqlid="Query_Where_Clause" ><where >1=1<choose><when test="id != null and id !=''">and      id = #{id}</when><whentest="brandName != null and brandName != ''">and      brand_name =  #{brandName}</when><whentest="logo != null and logo != ''">and      logo = #{logo}</when><whentest="firstChar != null and firstChar != ''">and      first_char =  #{firstChar}</when><whentest="status != null and status != ''">and      status = #{status}</when><whentest="active != null and active != ''">and      active = #{active}</when><whentest="createUser != null and createUser != ''">and      create_user =  #{createUser}</when><whentest="modifyUser != null and modifyUser != ''">and      modify_user =  #{modifyUser}</when><whentest="created != null and created != ''">and      created = #{created}</when><whentest="modified != null and modified != ''">and      modified =  #{modified}</when></choose></where></sql><select id="selectMallBrandByQuery" resultMap="ResultMap"parameterType="QueryMallBrand" >select<includerefid="ALL_TABLE_COLOUM" />from mall_brand<if test="page != null" ><include refid="Query_Where_Clause"/></if></select><select id="selectMallBrandByPage" resultMap="ResultMap"parameterType="QueryMallBrand" >select<includerefid="ALL_TABLE_COLOUM" />from mall_brand<if test="page != null"><includerefid="Query_Where_Clause" /></if>LIMIT #{startRow},#{pageSize}</select><select id="selectMallBrandById"resultMap="ResultMap" parameterType="java.lang.Long" >select<include refid="ALL_TABLE_COLOUM"/>from mall_brandwhere id = #{id}</select><delete id="deleteMallBrandById"parameterType="java.lang.Integer" >delete from mall_brandwhere id = #{id}</delete><delete id="deleteMallBrandByQuery" parameterType= "QueryMallBrand">delete from mall_brand<if test="page != null" ><includerefid="Query_Where_Clause" /></if></delete><insert id="insertMallBrand"parameterType="MallBrand" >INSERT INTOmall_brand(id,brand_name,logo,first_char,status,active,create_user,modify_user,created,modified)VALUES(#{id},#{brandName},#{logo},#{firstChar},#{status},#{active},#{createUser},#{modifyUser},#{created},#{modified})<selectKey resultType="long"keyProperty="id">SELECT @@IDENTITY AS ID</selectKey></insert><insert id="insertMallBrandModified" parameterType="MallBrand" >insert into mall_brand<trim prefix="("suffix=")" suffixOverrides="," ><if test="id != null">id,</if><if test="brandName !=null" >brand_name,</if><if test="logo !=null" >logo,</if><if test="firstChar !=null" >first_char,</if><if test="status !=null" >status,</if><if test="active !=null" >active,</if><if test="createUser !=null" >create_user,</if><if test="modifyUser !=null" >modify_user,</if><if test="created !=null" >created,</if><if test="modified !=null" >modified,</if></trim><trim prefix="values ("suffix=")" suffixOverrides="," ><if test="id != null" >#{id},</if><if test="brandName != null" >#{brandName},</if><if test="logo != null" >#{logo},</if><if test="firstChar != null" >#{firstChar},</if><if test="status != null" >#{status},</if><if test="active != null" >#{active},</if><if test="createUser != null" >#{createUser},</if><if test="modifyUser != null" >#{modifyUser},</if><if test="created != null" >now(),</if><if test="modified != null" >now(),</if></trim><selectKey resultType="long"keyProperty="id">SELECT @@IDENTITY AS ID</selectKey></insert><select id="countByQuery"parameterType="QueryMallBrand" resultType="java.lang.Long" >select count(*) from mall_brand<if test="page != null" ><includerefid="Query_Where_Clause" /></if></select><update id="updateMallBrandByIdModified" parameterType="MallBrand">update mall_brand<set ><iftest="brandName != null" >brand_name =  #{brandName},</if><iftest="logo != null" >logo =  #{logo},</if><iftest="firstChar != null" >first_char =  #{firstChar},</if><iftest="status != null" >status =  #{status},</if><iftest="active != null" >active =  #{active},</if><iftest="createUser != null" >create_user =  #{createUser},</if><iftest="modifyUser != null" >modify_user =  #{modifyUser},</if><iftest="created != null" >created =  #{created},</if><iftest="modified != null" >modified=now(),</if></set>where id = #{id}</update></mapper>

以上就是品牌管理功能的基本代码,看上去,这些代码确实很多,而且mapper文件的编写,往往容易出错,当然,mybatis也提供了官方工具——Mybatis Genrator帮你生成代码。但是Mybatis Genrator生成的东西,在命名上有些固定,而且会生成的查寻类太过冗余,生成的痕迹太强,真拿那样的代码用到工作中去,容易被吐槽的。

废话不多说了,我们先观察这中基础类别代码的一个规律——根据数据库字段,进行简单的新增,编辑,删除功能。对于数据操作,持久时离不开数据实体,也就是我们常常说的domain类,而为了数据查询,会往往需要一个单独的查询类进行封装。从某个层面来讲,这种代码生成程序的基本思路就找到了——查询数据库表结构->生成对应实体->生成dao需要的mapper->生成dao接口。

如何才能做到这些事情呢?还记得猿思考系列里,工厂君给你提及的一些事情吗?DatabaseMetaData这个东西还记得吗?为了在命名时更加规范,方法名可以更加灵活,还记得动态模板技术吗?现在前后端分离了,JSP、velocity、freemarker这类后端渲染技术是用得少了一些,但是不是说就没有使用场景了噢。你可以使用这些东西来生成HTML页面返回给浏览器,为什么就不能使用这些东西输出成你需要的.java文件还有.xml文件呢?技术这玩意儿,有时候还是需要一点想象力哒。

嗯,工厂君这套代码生成轮子,基本上就是这个套路了,DatabaseMetaData来解决数据库字段和java字段的映射问题,使用velocity的模板技术来渲染生成需要的代码文件。我们可以一起来看下。

数据库里的字段名,和java种的字段名往往不一致,要实现,数据库字段,到domain字段的映射,我们需要先对字段进行抽象,那么字段这个类自然会具有以下几个属性。

package com.pz.cg.gen;import java.net.MalformedURLException;
import java.net.URL;public class CgTest {}

/*** Copyright(c) 2004-2020 pangzi* GenerateEngine.java*/
package com.pz.cg;import com.pz.cg.db.ConnectionPool;
import com.pz.cg.db.Field;
import com.pz.cg.db.util.MetaDataUtil;
import com.pz.cg.gen.CgTest;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;import java.io.*;
import java.sql.*;
import java.util.*;public class GenerateEngine {protected static final String FIELDS ="fields";
protected static final String PACKAGE_NAME = "package";
protected static final String CLASS_NAME = "className";
protected static final String QUERY_CLASS_NAME = "queryClassName";
protected static final String TABLE_NAME = "tableName";
protected static final String INST_NAME= "instName";
protected static final String QUERY_INST_NAME = "queryInstName";
protected static final String IMPORTS ="imports";
protected static final String AUTHOR ="author";
protected static final String PK_ID ="pkid";
protected static final String PK_TYPE="pkType";
protected static final String PK_FILED="pkFiled";
protected static final String PK_SIMPLE_TYPE= "pkSimpleType";
protected static final String PK_NAME ="pkname";public static final String configFile ="config.properties";
protected static Properties prop = new Properties();
protected static Map<String,Object> context = null;
protected static Map<String,Object> commonContext = null;protected String tableName;
protected String className ="";
public String classType = "";
protected String queryClassType ="";
protected static final String ANOTATION= "annotation";
protected static final String DOMAIN ="domain";
protected static final String DAO ="dao";
protected static final String DAO_IMPL= "dao.impl";
protected static final String DAO_TEST= "dao.test";
protected static final String MANAGER ="manager";
protected static final String MANAGER_IMPL = "manager.impl";
protected static final String SERVICE ="service";
protected static final String SERVICE_IMPL = "service.impl";
protected static final String SQL_MAP ="sqlmap";
protected static final String QUERYDOMAIN = "querydomain";private String location;
private String fileName;
private String packagee;
private Set<String> imports = new HashSet<String>();
private String template;private static Logger log =Logger.getLogger(GenerateEngine.class);protected String getSecondDir() {if(StringUtils.isEmpty(prop.getProperty(PACKAGE_NAME + ".catalog"))) {return "";} else {return "."+ prop.getProperty(PACKAGE_NAME + ".catalog");}}
static {try {log.info("正在初始化环境...");context = new HashMap<String, Object>();File file =FileUtils.toFile(CgTest.class.getResource("/conf/config-local.properties"));prop.load(new FileInputStream(file));} catch (IOException e) {e.printStackTrace();log.info("始化环境失败",e);}}protected void init(String classType) {this.classType = classType;this.queryClassType ="query"+classType;if (commonContext == null) {initCommonContext();}initClassMetaInfo();intitLocation();this.setTemplate(prop.getProperty(classType));}
//根据配置完成数据库到字段的映射
protected void initCommonContext() {commonContext = new HashMap<String, Object>();List<Field> fields =new ArrayList<Field>();Connection conn = null;Statement st = null;ResultSet rs = null;Set<String> ip = null;Field pkField = new Field();String pkId = "";String pkName = "";String pkType = "";String pkSimpleType ="";Map<String, String>comment = new HashMap<String, String>();try {log.info("正在初始化表:"+ tableName + "元数据...");conn =ConnectionPool.getConnection();DatabaseMetaData dmd= conn.getMetaData();rs =dmd.getPrimaryKeys(null, null, tableName);if (rs.next()) {pkId =rs.getString("COLUMN_NAME");pkName =rs.getString("PK_NAME");}// 获取得列的注释rs =dmd.getColumns(null, null, tableName, null);int ix = 1;while(rs.next()) {comment.put(String.valueOf(ix),rs.getString("REMARKS"));ix += 1;}st =conn.createStatement();rs =st.executeQuery("select * from " + tableName);ResultSetMetaData meta = rs.getMetaData();Field field = null;String propertyName= "";String fieldName ="";String javaType ="";ip = new HashSet<String>();System.out.println(meta.getColumnCount());for (int i = 1; i<= meta.getColumnCount(); i++) {fieldName =meta.getColumnName(i);javaType =meta.getColumnClassName(i);propertyName= MetaDataUtil.fieldToProperty(fieldName);field = new Field();field.setPropertyName(propertyName);field.setFieldName(meta.getColumnName(i));field.setSetterName(MetaDataUtil.createSetterName(propertyName));field.setGetterName(MetaDataUtil.createGetterName(propertyName));field.setJavaType(MetaDataUtil.createJavaType(meta.getColumnClassName(i)));field.setJdbcType(meta.getColumnTypeName(i));field.setJavaFullType(meta.getColumnClassName(i));field.setComment(comment.get(String.valueOf(i)));fields.add(field);if(field.getJavaFullType().indexOf("java.lang") == -1) {ip.add(field.getJavaFullType());}//一定要放在最后if(pkId.equals(fieldName)) {pkType= javaType;pkSimpleType= MetaDataUtil.createJavaType(meta.getColumnClassName(i));if(pkSimpleType.equals("Integer")) {pkSimpleType= "int";}else if (pkSimpleType.equals("Long")) {pkSimpleType= "long";}pkField= field;}}} catch (Exception e) {e.printStackTrace();log.info("初始化表:" +tableName + "元数据失败", e);} finally {ConnectionPool.close(rs);ConnectionPool.close(st);ConnectionPool.close(conn);}commonContext.put(CLASS_NAME,className);commonContext.put(TABLE_NAME,tableName);commonContext.put(INST_NAME,className.substring(0, 1).toLowerCase() + className.substring(1));commonContext.put(QUERY_INST_NAME,"query"+className);commonContext.put(INST_NAME,className.substring(0, 1).toLowerCase() + className.substring(1));commonContext.put(FIELDS,fields);commonContext.put(PK_ID,pkId);commonContext.put(PK_SIMPLE_TYPE,pkSimpleType);commonContext.put(PK_NAME,pkName);commonContext.put(PK_TYPE,pkType);commonContext.put(PK_FILED,pkField);context.putAll(commonContext);if("true".equals(prop.getProperty(ANOTATION))) {context.put("Resource","@Resource");context.put("Component","@Component");}this.setImports(ip);log.info("元数据初始化完成.");System.out.println();}public void intitLocation() {String path =this.getPackagee();location =prop.getProperty(this.classType + ".out.path");// 为减少配置,config.properties中并没有配置impl的输出位置// 因此在生成impl类时需要替换其中中impl获取得其接口的输出位置if(StringUtils.isBlank(location)) {String str ="";// 替换掉impl用接口的输出位置// 如:在生成dao.impl时,实际上取的是配置文件中// dao.out.path的值if(this.classType.indexOf(".impl") != -1) {str =this.classType.substring(0, this.classType.indexOf(".impl"));}location =prop.getProperty(str + ".out.path");}// 除了sqlmap之外其它文件的输出位置均与package有关if(StringUtils.isNotBlank(path)) {path =path.replace(".", System.getProperty("file.separator"));location += System.getProperty("file.separator") + path;}location += System.getProperty("file.separator");try {FileUtils.forceMkdir(new File(location));} catch (IOException e) {e.printStackTrace();}}public void initClassMetaInfo() {context.put(PACKAGE_NAME,this.getPackagee());context.put(AUTHOR,prop.getProperty("class.author"));context.put("year",DateFormatUtils.format(System.currentTimeMillis(), "yyyy"));context.put("dateTime",DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-ddHH:mm:ss"));}public void generate() {try {log.info("正在生成 " +context.get(CLASS_NAME) + " -> " + this.classType + "代码...");Properties p = new Properties();p.put("resource.loader","file");p.put("file.resource.loader.class","org.apache.velocity.runtime.resource.loader.FileResourceLoader");p.put("file.resource.loader.path",FileUtils.toFile(CgTest.class.getResource("/com/pz/cg/vm/")).getAbsolutePath());p.put("input.encoding",prop.getProperty("in.encoding"));p.put("output.encoding",prop.getProperty("out.encoding"));Velocity.init(p);//Template template= Velocity.getTemplate("./resources/com/pz/cg/vm/" +this.getTemplate());Template template =Velocity.getTemplate(this.getTemplate());VelocityContext ctx= new VelocityContext(context);//Writer writer =new StringWriter();Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(this.getLocation() + "/" + this.getFileName())),prop.getProperty("out.encoding")));template.merge(ctx,writer);writer.flush();writer.close();log.info("生成 " +context.get(CLASS_NAME) + " -> " + this.classType + "代码结束.");log.info("输出位置:" +this.getLocation() + this.getFileName());System.out.println();} catch (Exception e) {e.printStackTrace();}}protected String getLocation() {return location;}protected void setLocation(String location) {this.location = location;}protected String getFileName() {return this.fileName;}protected void setFileName(String fileName) {this.fileName = fileName;}protected String getPackagee() {return packagee;}public void setPackagee(String packagee) {this.packagee = packagee;}protected Set<String>getImports() {return imports;}protected void setImports(Set<String> imports) {this.imports = imports;}protected String getTemplate() {return template;}protected void setTemplate(String template) {this.template = template;}public static Properties getProp() {return prop;}//清理common上下文
public static void clearContext() {commonContext = null;}}
/*** Copyright(c) 2004-2020 pangzi* Field.java*/
package com.pz.cg.db;public class Field {//java属性名private String propertyName;//字段名private String fieldName;//java数据完整类型private String javaFullType;//java数据类型private String javaType;//数据库字段类型private String jdbcType;//getter名private String getterName;//setter名private String setterName;//数据库字段注释private String comment;//长度private int length;}

既然已经提及到了需要通过数据库的来生成我们需要的代码,对于数据库的访问自然是少不了的,我们简单封装一个数据库连接池的工具类。

/*** Copyright(c) 2004-2020 pangzi* ConnectionPool.java*/
package com.pz.cg.db;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;import com.pz.cg.GenerateEngine;
import org.apache.log4j.Logger;public class ConnectionPool {private static Properties prop = newProperties();private static String userName;private static String password;private static String url;private static String driver;private static Logger log =Logger.ge tLogger(GenerateEngine.class);static {try {prop =GenerateEngine.getProp();userName =prop.getProperty("jdbc.username");password =prop.getProperty("jdbc.password");url =prop.getProperty("jdbc.url");driver =prop.getProperty("jdbc.driver");} catch (Exception ex) {ex.printStackTrace();}}public static void close(Connectionconn) {try {if (conn != null) {conn.close();conn =null;}} catch (Exception e) {e.printStackTrace();}}public static void close(Statement st){try {if (st != null) {st.close();st = null;}} catch (Exception e) {e.printStackTrace();}}public static void close(ResultSet rs){try {if (rs != null) {rs.close();rs = null;}} catch (Exception e) {e.printStackTrace();}}public static ConnectiongetConnection() {Connection con = null;log.info("正在连接到数据库...");try {Class.forName(driver);con =DriverManager.getConnection(url, userName, password);} catch (Exception e) {e.printStackTrace();log.info("连接数据库失败",e);}return con;}}

我们都知道,数据库字段的命名规范,往往是英文单词加下划线的形式出现的,而java字段的命名规范,往往是驼峰式的命名。为此了方便我们进行数据处理,我们封装一个工具类吧。


/*** Copyright(c) 2004-2020 pangzi* MetaDataUtil.java*/
package com.pz.cg.db.util;import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;import org.apache.commons.lang.CharUtils;
import org.apache.commons.lang.StringUtils;public class MetaDataUtil {public static Map<String, String>getTableColumns(Connection con, String tableName) {Map<String, String>columns = new HashMap<String, String>();return columns;}public static String createSetterName(StringfieldName) {return "set" +fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);}public static StringcreateGetterName(String fieldName) {return "get" +fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);}public static StringcreateJavaType(String dbJavaType) {String javaType =dbJavaType.substring(dbJavaType.lastIndexOf(".") + 1);//将jdbc的Timedstamp变为Date类型if("Timestamp".equals(javaType)) {javaType ="Date";}return javaType;}public static String propertyToField(Stringproperty) { if (null == property) { return ""; } char[] chars =property.toCharArray(); StringBuffer sb = newStringBuffer(); for (char c : chars) { if (CharUtils.isAsciiAlphaUpper(c)){ sb.append("_" +StringUtils.lowerCase(CharUtils.toString(c))); } else { sb.append(c); } } return sb.toString(); } public static String fieldToProperty(Stringfield) { if (null == field) { return ""; } char[] chars =field.toCharArray(); StringBuffer sb = newStringBuffer(); for (int i = 0; i < chars.length;i++) { char c = chars[i]; if (c == '_') { int j = i + 1; if (j < chars.length) { sb.append(StringUtils.upperCase(CharUtils.toString(chars[j]))); i++; } } else { sb.append(c); } } return sb.toString(); }}

好了,接下来,我们就要为生成代码做一些准备了,编写我们需要的核心代码了。不过在这之前,还有一些问题需要解决。比如,我们需要连接的数据库实例在哪里?我们需要生成的代码存放在哪里?代码的包结构如何定义?这些东西,为了灵活起见,我们定义一个配置文件吧。我们在resoures目录下建立一个conf/config-local.properties的配置文件。

## databaseconnection setting
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/pz_mall_basic?characterEncoding=utf-8
jdbc.username=root
jdbc.password=13456## encodingsetting ##
in.encoding=utf-8
out.encoding=utf-8## class metadata setting ##
class.author=pangzi
package=com.pz.basic.mall
##package.catalog=## packagesetting ##
package.domain=domain.brand
package.dao=dao.brand
package.manager=manager.brand
package.service=service.brand
package.action=controller.brand
package.querydomain=domain.brand.query## file out pathsetting ##
domain.out.path=D:/workspace-pangzi/pangzi-client/src/main/java
querydomain.out.path=D:/workspace-pangzi/pangzi-client/src/main/java
dao.out.path=D:/workspace-pangzi/pangzi-dao/src/main/java
manager.out.path=D:/workspace-pangzi/pangzi-manager/src/main/java
sqlmap.out.path=D:/workspace-pangzi/pangzi-dao/src/main/resources/sqlmapservice.out.path=D:/workspace-pangzi/pangzi-service/src/main/javaaction.out.path=D:/workspace-pangzi/pangzi-controller/src/main/java## code templatesetting ##
## domain ##
domain=pojo.vm
## query##
querydomain=querypojo.vm
##分页##
querybase=PaginateBaseDO.vm
##dao##
dao=dao.vm
##dao实现类##
dao.impl=dao.impl.vm
##测试类##
dao.test=
##manager##
manager=manager.vm
##manager实现##
manager.impl=manager.impl.vm
##service##
service=service.vm
##service实现##
service.impl=service.impl.vm
##mapper##
sqlmap=sql_map.vm

/*** Copyright(c) 2004-2020 pangzi* GenerateEngine.java*/
package com.pz.cg;import com.pz.cg.db.ConnectionPool;
import com.pz.cg.db.Field;
import com.pz.cg.db.util.MetaDataUtil;
import com.pz.cg.test.CgTest;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;import java.io.*;
import java.sql.*;
import java.util.*;public classGenerateEngine {protected static final String FIELDS ="fields";protected static final StringPACKAGE_NAME = "package";protected static final StringCLASS_NAME = "className";protected static final StringQUERY_CLASS_NAME = "queryClassName";protected static final StringTABLE_NAME = "tableName";protected static final String INST_NAME= "instName";protected static final StringQUERY_INST_NAME = "queryInstName";protected static final String IMPORTS ="imports";protected static final String AUTHOR ="author";protected static final String PK_ID ="pkid";protected static final String PK_TYPE="pkType";protected static final String PK_FILED="pkFiled";protected static final StringPK_SIMPLE_TYPE= "pkSimpleType";protected static final String PK_NAME ="pkname";public static final String configFile ="config.properties";protected static Properties prop = newProperties();protected static Map<String,Object> context = null;protected static Map<String,Object> commonContext = null;protected String tableName;protected String className ="";public String classType = "";protected String queryClassType ="";protected static final String ANOTATION= "annotation";protected static final String DOMAIN ="domain";protected static final String DAO ="dao";protected static final String DAO_IMPL= "dao.impl";protected static final String DAO_TEST= "dao.test";protected static final String MANAGER ="manager";protected static final StringMANAGER_IMPL = "manager.impl";protected static final String SERVICE ="service";protected static final StringSERVICE_IMPL = "service.impl";protected static final String SQL_MAP ="sqlmap";protected static final StringQUERYDOMAIN = "querydomain";private String location;private String fileName;private String packagee;private Set<String> imports = newHashSet<String>();private String template;private static Logger log =Logger.getLogger(GenerateEngine2.class);protected String getSecondDir() {if(StringUtils.isEmpty(prop.getProperty(PACKAGE_NAME + ".catalog"))) {return "";} else {return "."+ prop.getProperty(PACKAGE_NAME + ".catalog");}}static {try {log.info("正在初始化环境...");context = newHashMap<String, Object>();File file =FileUtils.toFile(CgTest.class.getResource("/conf/config-local.properties"));prop.load(newFileInputStream(file));} catch (IOException e) {e.printStackTrace();log.info("始化环境失败",e);}}protected void init(String classType) {this.classType = classType;this.queryClassType ="query"+classType;if (commonContext == null) {initCommonContext();}initClassMetaInfo();intitLocation();this.setTemplate(prop.getProperty(classType));}//根据配置完成数据库到字段的映射protected void initCommonContext() {commonContext = newHashMap<String, Object>();List<Field> fields =new ArrayList<Field>();Connection conn = null;Statement st = null;ResultSet rs = null;Set<String> ip = null;Field pkField = new Field();String pkId = "";String pkName = "";String pkType = "";String pkSimpleType ="";Map<String, String>comment = new HashMap<String, String>();try {log.info("正在初始化表:"+ tableName + "元数据...");conn =ConnectionPool.getConnection();DatabaseMetaData dmd= conn.getMetaData();rs =dmd.getPrimaryKeys(null, null, tableName);if (rs.next()) {pkId =rs.getString("COLUMN_NAME");pkName =rs.getString("PK_NAME");}// 获取得列的注释rs =dmd.getColumns(null, null, tableName, null);int ix = 1;while(rs.next()) {comment.put(String.valueOf(ix),rs.getString("REMARKS"));ix += 1;}st =conn.createStatement();rs =st.executeQuery("select * from " + tableName);ResultSetMetaDatameta = rs.getMetaData();Field field = null;String propertyName= "";String fieldName ="";String javaType ="";ip = newHashSet<String>();System.out.println(meta.getColumnCount());for (int i = 1; i<= meta.getColumnCount(); i++) {fieldName =meta.getColumnName(i);javaType =meta.getColumnClassName(i);propertyName= MetaDataUtil.fieldToProperty(fieldName);field = newField();field.setPropertyName(propertyName);field.setFieldName(meta.getColumnName(i));field.setSetterName(MetaDataUtil.createSetterName(propertyName));field.setGetterName(MetaDataUtil.createGetterName(propertyName));field.setJavaType(MetaDataUtil.createJavaType(meta.getColumnClassName(i)));field.setJdbcType(meta.getColumnTypeName(i));field.setJavaFullType(meta.getColumnClassName(i));field.setComment(comment.get(String.valueOf(i)));fields.add(field);if(field.getJavaFullType().indexOf("java.lang") == -1) {ip.add(field.getJavaFullType());}//一定要放在最后if(pkId.equals(fieldName)) {pkType= javaType;pkSimpleType= MetaDataUtil.createJavaType(meta.getColumnClassName(i));if(pkSimpleType.equals("Integer")) {pkSimpleType= "int";}else if (pkSimpleType.equals("Long")) {pkSimpleType= "long";}pkField= field;}}} catch (Exception e) {e.printStackTrace();log.info("初始化表:" +tableName + "元数据失败", e);} finally {ConnectionPool.close(rs);ConnectionPool.close(st);ConnectionPool.close(conn);}commonContext.put(CLASS_NAME,className);commonContext.put(TABLE_NAME,tableName);commonContext.put(INST_NAME,className.substring(0, 1).toLowerCase() + className.substring(1));commonContext.put(QUERY_INST_NAME,"query"+className);commonContext.put(INST_NAME,className.substring(0, 1).toLowerCase() + className.substring(1));commonContext.put(FIELDS,fields);commonContext.put(PK_ID,pkId);commonContext.put(PK_SIMPLE_TYPE,pkSimpleType);commonContext.put(PK_NAME,pkName);commonContext.put(PK_TYPE,pkType);commonContext.put(PK_FILED,pkField);context.putAll(commonContext);if("true".equals(prop.getProperty(ANOTATION))) {context.put("Resource","@Resource");context.put("Component","@Component");}this.setImports(ip);log.info("元数据初始化完成.");System.out.println();}public void intitLocation() {String path =this.getPackagee();location =prop.getProperty(this.classType + ".out.path");// 为减少配置,config.properties中并没有配置impl的输出位置// 因此在生成impl类时需要替换其中中impl获取得其接口的输出位置if(StringUtils.isBlank(location)) {String str ="";// 替换掉impl用接口的输出位置// 如:在生成dao.impl时,实际上取的是配置文件中// dao.out.path的值if(this.classType.indexOf(".impl") != -1) {str =this.classType.substring(0, this.classType.indexOf(".impl"));}location =prop.getProperty(str + ".out.path");}// 除了sqlmap之外其它文件的输出位置均与package有关if(StringUtils.isNotBlank(path)) {path =path.replace(".", System.getProperty("file.separator"));location += System.getProperty("file.separator") + path;}location += System.getProperty("file.separator");try {FileUtils.forceMkdir(newFile(location));} catch (IOException e) {e.printStackTrace();}}public void initClassMetaInfo() {context.put(PACKAGE_NAME,this.getPackagee());context.put(AUTHOR,prop.getProperty("class.author"));context.put("year",DateFormatUtils.format(System.currentTimeMillis(), "yyyy"));context.put("dateTime",DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-ddHH:mm:ss"));}public void generate() {try {log.info("正在生成 " +context.get(CLASS_NAME) + " -> " + this.classType + "代码...");Properties p = newProperties();p.put("resource.loader","file");p.put("file.resource.loader.class","org.apache.velocity.runtime.resource.loader.FileResourceLoader");p.put("file.resource.loader.path",FileUtils.toFile(CgTest.class.getResource("/com/pz/cg/vm/")).getAbsolutePath());p.put("input.encoding",prop.getProperty("in.encoding"));p.put("output.encoding",prop.getProperty("out.encoding"));Velocity.init(p);//Template template= Velocity.getTemplate("./resources/com/pz/cg/vm/" +this.getTemplate());Template template =Velocity.getTemplate(this.getTemplate());VelocityContext ctx= new VelocityContext(context);//Writer writer =new StringWriter();Writer writer = newBufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile(this.getLocation() + "/" + this.getFileName())),prop.getProperty("out.encoding")));template.merge(ctx,writer);writer.flush();writer.close();log.info("生成 " +context.get(CLASS_NAME) + " -> " + this.classType + "代码结束.");log.info("输出位置:" +this.getLocation() + this.getFileName());System.out.println();} catch (Exception e) {e.printStackTrace();}}protected String getLocation() {return location;}protected void setLocation(Stringlocation) {this.location = location;}protected String getFileName() {return this.fileName;}protected void setFileName(StringfileName) {this.fileName = fileName;}protected String getPackagee() {return packagee;}public void setPackagee(Stringpackagee) {this.packagee = packagee;}protected Set<String>getImports() {return imports;}protected voidsetImports(Set<String> imports) {this.imports = imports;}protected String getTemplate() {return template;}protected void setTemplate(Stringtemplate) {this.template = template;}public static Properties getProp() {return prop;}//清理common上下文public static void clearContext() {commonContext = null;}
}

输入输出的核心问题解决了,我们可以看看具体到每一种类型的代码生成了,我们为每一种需要的类型,编写一种具体的生成器就可以了。

package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;/*** 生成一些基本类,例如queryDO,resultDO等* @author pangzi**/
public class BaseGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {//由PaginateBaseDO.vm生成PaginateBaseDO.javathis.classType = QUERYDOMAIN;this.setTemplate("PaginateBaseDO.vm");this.setFileName("PaginateBaseDO.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain"));initClassMetaInfo();intitLocation();         this.generate();//由Result.vm生成Result.javathis.classType = DOMAIN;this.setTemplate("Result.vm");this.setFileName("Result.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base" );this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();//生成BaseDOthis.classType = DOMAIN;this.setTemplate("BaseDO.vm");this.setFileName("BaseDO.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base");this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();//生成输出文件格式this.classType = DOMAIN;this.setTemplate("NoNullFieldStringStyle.vm");this.setFileName("NoNullFieldStringStyle.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base" );this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();                }public Set<String>getResultImport() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)+ ".domain.base"  +".PaginateBaseDO" );context.put(IMPORTS,imports);return imports;}
}
/*** Copyright(c) 2004-2020 pangzi* com.pz.cg.gen.PojoCodeGenerator.java*/
package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;/*** @author pangzi**/
public class PojoGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +this.getSecondDir());this.init(DOMAIN);context.put(IMPORTS,this.getPojoImports());this.generate();}protected Set<String>getPojoImports() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)  + ".domain.base.BaseDO");return imports;}
}

/****/
package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;/*** @author pangzi**/
public class QueryPojoGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName("Query"+this.className + ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain") +  this.getSecondDir());this.setImports(getImport());this.init(QUERYDOMAIN);context.put(IMPORTS,this.getImports());this.generate();}public Set<String> getImport() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)+ ".domain.base.PaginateBaseDO" );context.put(IMPORTS,imports);return imports;}
}
/*** Copyright(c) 2004-2020 pangzi* com.pz.cg.gen.DaoCodeGenerator.java*/
package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;public class DaoGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ "Dao" + ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.dao") +this.getSecondDir());this.setImports(this.getDaoImport());this.init(DAO);this.generate();this.generateImpl(tableName,className);}private void generateImpl(StringtableName, String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ "DaoImpl" + ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.dao") +  this.getSecondDir() + ".impl");this.setTemplate(prop.getProperty("dao.impl"));this.setImports(this.getDaoImplImport());this.init(DAO_IMPL);this.generate();}public Set<String> getDaoImport(){Set<String> imports =new HashSet<String>();imports.add("java.util.List");imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain")+this.getSecondDir()  + "."+"Query"+ className);imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +this.getSecondDir()  + "." +className);context.put(IMPORTS,imports);return imports;}public Set<String>getDaoImplImport() {Set<String> imports =new HashSet<String>();imports.add("java.util.List");imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +  this.getSecondDir()  + "." + className);imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain")+this.getSecondDir()  + ".Query"+ className);imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.dao") +  this.getSecondDir()  + "." + className +"Dao");context.put(IMPORTS,imports);return imports;}}
package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;public class ServiceGenerator extends GenerateEngine implements    CodeGenerator {public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ "Service" + ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.service") +this.getSecondDir());this.setImports(this.getServiceImport());this.init(SERVICE);this.generate();this.generateImpl(tableName,className);}private void generateImpl(StringtableName, String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ "ServiceImpl" + ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.service") +  this.getSecondDir() + ".impl");this.setTemplate(prop.getProperty("service.impl"));this.setImports(this.getServiceImplImport());this.init(SERVICE_IMPL);this.generate();}public Set<String>getServiceImport() {Set<String> imports =new HashSet<String>();imports.add("java.util.List");imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +".Result" );            imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +this.getSecondDir()  + "." +className);imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain")+this.getSecondDir()  + ".Query"+ className);context.put(IMPORTS,imports);return imports;}public Set<String>getServiceImplImport() {Set<String> imports =new HashSet<String>();imports.add("java.util.List");imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +".Result" );            imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +this.getSecondDir()  + "." +className);imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain")+this.getSecondDir()  + ".Query"+ className);imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.dao") +this.getSecondDir()  + "." +className + "Dao");imports.add(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.service") +this.getSecondDir() + "." + className + "Service");context.put(IMPORTS,imports);return imports;}}
package com.pz.cg.gen;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;public class SqlMapGenerator extends GenerateEngine implements CodeGenerator {public static String SQL_DOMAIN ="sqlDomain";public static String SQL_QUERYDOMAIN ="sqlQueryDomain";public static String DAO_DOMAIN ="daoDomain";public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ "Mapper.xml");//设置sqlmap的domain和querydomain 的packagecontext.put(SQL_DOMAIN,prop.getProperty(PACKAGE_NAME) + "." +prop.getProperty("package.domain") + this.getSecondDir()  + "." + className);context.put(SQL_QUERYDOMAIN,prop.getProperty(PACKAGE_NAME) + "." +prop.getProperty("package.querydomain")+ this.getSecondDir()  + ".Query" + className);context.put(DAO_DOMAIN,prop.getProperty(PACKAGE_NAME) + "." +prop.getProperty("package.dao") + this.getSecondDir()  + "." + className);this.init(SQL_MAP);this.generate();}}

接下来就是你的重中之重的事情来了——为每一种你需要的代码,编写具体的vm模板文件。

Domian的基类模板 BaseDO.vm:
/*** Copyright(c) 2004-$!year pangzi* $!{package}.$!{className}.java*/
package $!package;import java.io.Serializable;import org.apache.commons.lang3.builder.ToStringBuilder;import $!{package}.NoNullFieldStringStyle;/**** @author $!author* @date $!dateTime***/
public class BaseDO implements Serializable {private static final longserialVersionUID = 1L;/*** 如果字段值为null将不包含在toString中*/@Overridepublic String toString() {returnToStringBuilder.reflectionToString(this);}
}

查询基础类 模板 PaginateBaseDO.vm:


/**
* Copyright(c)2004-$!year pangzi
*$!{package}.PaginateBaseDO.java
*/
package $!{package};
import java.io.Serializable;
/**
*
* @author$!author
* @date$!dateTime
*
*
*/
public class PaginateBaseDO{/**
* 默认每页的记录数量
*/
public staticfinal int PAGESIZE_DEFAULT = 20;
/**
* 每页大小
*/
private int pageSize;
/**
* 当前页。第一页是1
*/
private int index;/**
* 总记录数
*/
private int totalItem;
/**
* 总页数
*/
private int totalPage;/**
* 分页后的记录开始的地方
* 第一条记录是1
*/
private int startRow;
/**
* 分页后的记录结束的地方
*/
private int endRow;/**排序字段**/
private String orderField;/**升序 还是 降序,true为升序,false为降序*/
private Boolean isAsc;/**
* 默认构造方法
*/
public PaginateBaseDO() {repaginate();
}/**
* 带当前页和页大小的构造方法
* @param index 当前页
* @parampageSize 页大小
*/
public PaginateBaseDO(int index, int pageSize) {this.index =index;
this.pageSize =pageSize;
repaginate();
}public void setStartRow(int startRow) {this.startRow =startRow;
}public void setEndRow(int endRow) {this.endRow =endRow;
}/**
* 表示是不是第一页
* @return true 是; false 不是
*/
public boolean isFirstPage(){return index<= 1;
}public boolean isMiddlePage() {return!(isFirstPage() || isLastPage());
}public boolean isLastPage() {return index>= totalPage;
}public boolean isNextPageAvailable() {return!isLastPage();
}public boolean isPreviousPageAvailable() {return!isFirstPage();
}/**
* 下一页号
* @return 取得下一页号
*/
public int getNextPage() {if(isLastPage()){returntotalItem;
} else {return index+1;
}
}public int getPreviousPage() {if(isFirstPage()){return 1;
} else {return index -1;
}
}
/**
* MethodgetPageSize returns the pageSize of this PaginatedArrayList object.
*
*  每页大小
*
* @return thepageSize (type int) of this PaginatedArrayList object.
*/public int getPageSize() {return pageSize;
}/**
* MethodsetPageSize sets the pageSize of this PaginatedArrayList object.
*
*  每页大小
*
* @parampageSize the pageSize of this PaginatedArrayList object.
*
*/public void setPageSize(int pageSize) {this.pageSize =pageSize;
repaginate();
}/**
* MethodgetIndex returns the index of this PaginatedArrayList object.
*
*  当前页。第一页是1
*
* @return theindex (type int) of this PaginatedArrayList object.
*/public int getIndex() {return index;
}/**
* MethodsetIndex sets the index of this PaginatedArrayList object.
*
*  当前页。第一页是1
*
* @param indexthe index of this PaginatedArrayList object.
*
*/public void setIndex(int index) {this.index =index;
repaginate();
}/**
* MethodgetTotalItem returns the totalItem of this PaginatedArrayList object.
*
*  总记录数
*
* @return thetotalItem (type int) of this PaginatedArrayList object.
*/public int getTotalItem() {returntotalItem;
}/**
* MethodsetTotalItem sets the totalItem of this PaginatedArrayList object.
*
*  总记录数
*
* @paramtotalItem the totalItem of this PaginatedArrayList object.
*
*/public void setTotalItem(int totalItem) {this.totalItem =totalItem;
if(this.totalItem <= 0){totalPage = 0;
index = 1;
startRow = 0;
}
repaginate();
}/**
* MethodgetTotalPage returns the totalPage of this PaginatedArrayList object.
*
*  总页数
*
* @return thetotalPage (type int) of this PaginatedArrayList object.
*/public int getTotalPage() {return totalPage;
}/**
* MethodgetStartRow returns the startRow of this PaginatedArrayList object.
*
*  分页后的记录开始的地方
*
* @return thestartRow (type int) of this PaginatedArrayList object.
*/public int getStartRow() {if (startRow> 0) {return startRow;
}
if (index <=0) {index = 1;
}
return (index -1) * pageSize;
}/**
* MethodgetEndRow returns the endRow of this PaginatedArrayList object.
*
*  分页后的记录结束的地方
*
* @return theendRow (type int) of this PaginatedArrayList object.
*/public int getEndRow() {if (endRow >0) {return endRow;
}
return index *pageSize;
}public String getOrderField() {returnorderField;
}public void setOrderField(String orderField) {this.orderField= orderField;
}public Boolean getIsAsc() {return isAsc;
}public void setIsAsc(Boolean isAsc) {this.isAsc =isAsc;
}/**
* Methodrepaginate ...
*/
public void repaginate() {if (pageSize< 1) { //防止程序偷懒,list和分页的混合使用
pageSize =PAGESIZE_DEFAULT;
}
if (index <1) {index = 1;//恢复到第一页
}
if (totalItem> 0) {totalPage =totalItem / pageSize + (totalItem % pageSize > 0 ? 1 : 0);
if(index >totalPage) {index =totalPage; //最大页
}
endRow = index *pageSize;
startRow =(index - 1) * pageSize;
if(endRow>totalItem){endRow =totalItem;
}
}
}
}

Domain 模板 pojo.vm:

/*** Copyright(c) 2004-$!year pangzi* $!{package}.$!{className}.java*/
package $!package;import java.util.Date;
#foreach($importin $imports)
import $import;
#end/**** @author $!author* @date $!dateTime**/
public class $!className extends BaseDO {#foreach($fieldin $fields)/**$!field.comment**/private $!field.javaType$!field.propertyName;#endpublic $!className() {}#foreach($fieldin $fields)public $!field.javaType$field.getGetterName()() {return $field.propertyName;}public void $field.getSetterName()($!field.javaType $!field.propertyName) {this.$!field.propertyName =$!field.propertyName;}#end}

Query 模板 querypojo.vm:


/*** Copyright(c) 2004-$!year pangzi* $!{package}.$!{className}.java*/
package $!package;
import java.util.Date;
#foreach($importin $imports)
import $import;
#end/**** @author $!author* @date $!dateTime***/
public class Query$!className extends PaginateBaseDO {#foreach($fieldin $fields)/**$!field.comment**/private $!field.javaType$!field.propertyName;#end#foreach($fieldin $fields)public $!field.javaType$field.getGetterName()() {return $field.propertyName;}public void $field.getSetterName()($!field.javaType $!field.propertyName) {this.$!field.propertyName =$!field.propertyName;}#end}

Dao 模板 dao.vm:

/*** Copyright(c) 2004-$!year pangzi* $!{package}.$!{className}Dao.java*/
package $!{package};
#foreach($importin $imports)
import $import;
#end
import java.util.Map;
import java.io.Serializable;/**** @author $!author* @date $!dateTime*/
public interface $!{className}Dao {/*** 根据条件查询总数* @param Query$!{className} query* @return*/$!pkSimpleType countByQuery(Query$!{className} query);/*** 根据条件删除记录* @param $!{className}Query query* @return*/int delete$!{className}ByQuery(Query$!{className} query);/*** 根据ID删除记录* @param id* @return*/int delete$!{className}ById($!pkSimpleType$!{pkid});/*** 新增记录* @param $!{className} record* @return*/$!pkSimpleType insert$!{className}($!{className} record);/*** 新增记录 注意:有值的记录才新增* @param $!{className} record* @return*/$!pkSimpleType insert$!{className}Modified($!{className} record);/*** 根据查询条件返回列表* @param Query$!{className} query* @return*/List<$!{className}> select$!{className}ByQuery(Query$!{className} query);/*** 根据查询条件返回列表* @param Query$!{className} query* @return*/List<$!{className}> select$!{className}ByPage(Query$!{className} query);/*** 根据ID查询对象* @param Long id* @return*/$!{className} select$!{className}ById($!pkSimpleType $!{pkid});/*** 根据id修改记录 注意:有值的字段才更新* @param $!{className} record* @return*/int update$!{className}ByIdModified($!{className}record);}

mapper 模板sql_map.vm:


#set($mapName ="$!{className.toUpperCase()}-MAP")
#set($insertSql= "")
#set($insertFields= "")
#set($pageCommonSql= "PAGE-COMMON")
#set($queryCommonSql= "QUERY-COMMON")
#set($exampleCommonSql= "EXAMPLE-COMMON")
<?xmlversion="1.0" encoding="UTF-8" ?>
<!DOCTYPEmapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mappernamespace="$!{daoDomain}Dao"><typeAliasalias="$!{className}" type="$!{sqlDomain}"/><typeAliasalias="Query$!{className}" type="$!{sqlQueryDomain}"/><resultMap id="ResultMap"type="$!{className}">#foreach($field in $fields)<id property="$!{field.propertyName}"column="$!{field.fieldName}"/>#set($insertSql =$insertSql + "#{" + $!{field.propertyName} + "}" +",")#set($insertFields =$insertFields + $!{field.fieldName} + ",")#end#if($!$insertSql.endsWith(","))#set($insertSql =$!insertSql.substring(0, $insertSql.lastIndexOf(",")))#end#if($!$insertFields.endsWith(","))#set($insertFields =$!insertFields.substring(0, $insertFields.lastIndexOf(",")))#end</resultMap><sqlid="ALL_TABLE_COLOUM">   #set($index=1)#foreach($field in $fields)#if($index==$fields.size())$!{field.fieldName}#else$!{field.fieldName},#end#set($index =$index+1)#end</sql><sqlid="Query_Where_Clause" ><where >1=1#foreach($field in $fields)<iftest="$!{field.propertyName} != null and $!{field.propertyName} !=''">and $!field.fieldName = #{$!{field.propertyName}}</if>#end</where></sql><select id="select$!{className}ByQuery"resultMap="ResultMap" parameterType="Query$!{className}">select<includerefid="ALL_TABLE_COLOUM" />from $!{tableName}<if test="page != null" ><includerefid="Query_Where_Clause" /></if></select><selectid="select$!{className}ByPage" resultMap="ResultMap"parameterType="Query$!{className}" >select<includerefid="ALL_TABLE_COLOUM" />from $!{tableName}<if test="page != null"><includerefid="Query_Where_Clause" /></if>LIMIT #{startRow},#{pageSize}</select><selectid="select$!{className}ById" resultMap="ResultMap"parameterType="$!pkSimpleType" >select<includerefid="ALL_TABLE_COLOUM" />from $!{tableName}where $!pkFiled.fieldName =#{$!pkFiled.propertyName}</select><deleteid="delete$!{className}ById" parameterType="$!pkSimpleType">delete from $!{tableName}where $!pkFiled.fieldName =#{$!pkFiled.propertyName}</delete><deleteid="delete$!{className}ByQuery" parameterType="Query$!{className}" >delete from $!{tableName}<if test="page != null" ><includerefid="Query_Where_Clause" /></if></delete><insert id="insert$!{className}"parameterType="$!{className}" >INSERT INTO$!{tableName}($!insertFields)VALUES($!insertSql)<selectKeyresultType="$!pkSimpleType"keyProperty="$!pkFiled.propertyName">SELECT @@IDENTITY AS ID</selectKey></insert><insertid="insert$!{className}Modified"parameterType="$!{className}" >insert into $!{tableName}<trim prefix="("suffix=")" suffixOverrides="," >#foreach($field in $fields)#if($!field.propertyName=='created'||$!field.propertyName=='modified')<iftest="$!field.propertyName == null" >$!{field.fieldName},</if>#else<iftest="$!field.propertyName != null" >$!{field.fieldName},</if>#end#end</trim><trim prefix="values ("suffix=")" suffixOverrides="," >#foreach($field in $fields)#if($!field.propertyName=='created'||$!field.propertyName=='modified')<iftest="$!field.propertyName == null" >now(),</if>#else<iftest="$!field.propertyName != null" >#{$!{field.propertyName}},</if>#end#end</trim><selectKeyresultType="$!pkSimpleType"keyProperty="$!pkFiled.propertyName">SELECT @@IDENTITY AS ID</selectKey></insert><select id="countByQuery"parameterType="Query$!{className}" resultType="java.lang.Long" >select count(*) from $!{tableName}<if test="page != null" ><includerefid="Query_Where_Clause" /></if></select><update id="update$!{className}ByIdModified"parameterType="$!{className}" >update $!{tableName}<set >#foreach($field in $fields)#if($!field.fieldName!=$!pkFiled.fieldName)#if($!field.propertyName=='modified')<if test="$!field.propertyName!= null" >$!field.fieldName=now(),</if>#else<iftest="$!field.propertyName != null" >$!field.fieldName =  #{$!{field.propertyName}},</if>#end#end#end</set>where $!pkFiled.fieldName =#{$!pkFiled.propertyName}</update></mapper>

以上的模板,基本上解决了实际开发的需要。由于Mybatis框架使用代理模式的特性,无需再编写对应的实现类了。不过需要注意的是,dao接口中的方法名,需要于mapper文件中的id保持一致,如果因为需要,需要更换命名规范,同步修改即可_

Dao层之上的代码,比如service,比如manager这类代码是需要根据业务代码来具体编写的,一些简单的封装我们还是可以做的,比如说分页这类事情。为此,我们为了返回结果的需要,需要定义一个通用点的返回类,模板如下。

结果返回模板:Result.vm

/*** Copyright(c) 2004-$!year pangzi* $!{package}.Result.java*/
package $!{package};import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
#foreach($importin $imports)
import $import;
#end/**** @author $!author* @date $!dateTime***/
public class Result<T> extends PaginateBaseDO implements Serializable {/****/private static final longserialVersionUID = 6028636097083630372L;/*** 是否成功*/private boolean success = true;/*** service返回的对象*/private Map<String, Object>result = new HashMap<String, Object>();/*** 默认的key*/public static final StringDEFAULT_MODEL_KEY = "value";/*** 当前的key*/private String modelKey =DEFAULT_MODEL_KEY;private T module;/*** 返回码*/private String resultCode;private String[] resultCodeParams;/*** 带是否成功的构造方法** @param success*/public Result(boolean success) {this.success = success;}/*** 默认构造方法*/public Result() {}/*** 新增一个返回结果** @param obj* @return*/public Object addDefaultModel(T obj) {return module = obj;}/*** 得到默认的模型* @return*/public T getModel(){return module;}/*** 新增一个带key的返回结果** @param key* @param obj* @return*/public Object addDefaultModel(Stringkey, Object obj) {modelKey = key;return result.put(key, obj);}/*** 取出所有的key** @return*/public Set<String> keySet() {return result.keySet();}/*** 取出默认的值** @return*/public Object get() {return result.get(modelKey);}/*** 取出值** @param key* @return*/public Object get(String key) {return result.get(key);}/*** 取出值集合** @return*/public Collection values() {return result.values();}/*** 返回是否成功** @return*/public boolean getSuccess() {return success;}public boolean isSuccess() {return success;}/*** 设置返回是否成功** @param success*/public void setSuccess(boolean success){this.success = success;}public String getResultCode() {return resultCode;}public void setResultCode(StringresultCode) {this.resultCode = resultCode;}public void setResultCode(StringresultCode, String... args) {this.resultCode = resultCode;this.resultCodeParams = args;}public String[] getResultCodeParams() {return resultCodeParams;}public void setResultCodeParams(String[] resultCodeParams) {this.resultCodeParams =resultCodeParams;}
}

service层的代码,对于业务相对简单来说的系统来说,主要解决的是一个分页的问题,我们看看如下图所示。

我们可以十分方便的将MallBrand替换成我们需要的实体就可以了,于是我们可以较为快速的编写初需要的模板。

Service 模板service.vm:


/*** Copyright(c) 2004-$!year pangzi* $!{package}.$!{className}Service.java*/
package $!{package};
#foreach($importin $imports)
import $import;
#end
import java.util.Map;
import java.io.Serializable;/*** service层,组装外部接口 和 本地业务,为本业务 或者其他业务提供服务,统一返回Result* 通过Result.isSuccess判断调用是否成功* 此类中新增业务接口设计(接口命令,入参数据,返回值)要 能尽量完整的表达业务 含义* @author $!author* @date $!dateTime*/
public interface $!{className}Service {/*** 新增 $!instName* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getModel()得到新增$!instName* @param $!instName* @return*/public Result<$!className> add$!{className}($!className $!instName) ;/*** 按照主键id更新$!instName,请重新new$!className 的更新对象,设置要更新的字段* 返回result,通过result.isSuccess()判断更新是否成功* @param id* @param $!instName* @return*/public Result update$!{className}ById($!className $!instName);/*** 按照主键id 删除 记录* 返回result,通过result.isSuccess()判断删除是否成功* @param id* @return*/public Result delete$!{className}ById($!pkSimpleType $!{pkid});/*** 查询列表,此接口不包含分页查询* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getModel()得到列表信息* @param $!queryInstName* @return*/publicResult<List<$!className>> get$!{className}sByQuery(Query$!className$!queryInstName);/*** 通过主键id查询$!className* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getModel()得到查询的单条$!instName信息* @param id* @return*/public Result<$!className> get$!{className}ById($!pkSimpleType $!{pkid});/*** 查询列表,包含分页查询* 查询分页信息,请设置* Query$!{className}.setIndex(设置当前页数)* Query$!{className}.setPageSize(设置当前页面数据行数)* 返回result,通过result.isSuccess()判断服务调用是否成功* 通过result.getTotal()返回结果总数* 通过result.getModel()得到查询的单页列表信息* @param $!queryInstName* @return*/public Result<List<$!className>> get$!{className}sByPage(Query$!className$!queryInstName);/*** 查询总数* @param $!queryInstName* @return*/public Result<$!pkType> count(Query$!className $!queryInstName);}

Service.impl模板service.impl.vm:


#set($managerName= "$!{instName}Manager")
#set($daoName ="$!{instName}Dao")/*** Copyright(c) 2004-$!year pangzi* $!{package}.$!{className}Service.java*/
package $!{package};
#foreach($importin $imports)
import $import;
#end
import java.util.ArrayList;
#if($!{Resource})
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
#end
import java.util.Map;
import java.io.Serializable;/**** @author $!author* @date $!dateTime*/
#if($!{Resource})
$!Component
#end
public class $!{className}ServiceImpl implements $!{className}Service {$!{Resource}private $!{className}Dao $!{daoName};#if(!$!{Resource})public void set$!{className}Dao($!{className}Dao $!{daoName}) {this.$!{daoName} = $!{daoName};#endpublic Result<$!className> add$!{className}($!className $!instName) {Result<$!className>result = new Result<$!className>();try {$!{daoName}.insert$!{className}Modified($!instName);result.addDefaultModel($!instName);} catch(Exception e) {result.setSuccess(false);}return result;}public Result update$!{className}ById($!className $!instName) {Result result = new Result();try {int count=$!{daoName}.update$!{className}ByIdModified($!instName);if(count>0){result.setSuccess(true);}} catch(Exception e) {result.setSuccess(false);}return result;}public Result delete$!{className}ById($!pkSimpleType $!{pkid}) {Result result = new Result();try {int count=$!{daoName}.delete$!{className}ById($!{pkid});if(count>0){result.setSuccess(true);}} catch(Exception e) {result.setSuccess(false);}return result;}       public Result<List<$!className>> get$!{className}sByQuery(Query$!className$!queryInstName) {Result<List<$!className>> result = new Result<List<$!className>>();try {result.addDefaultModel($!{daoName}.select$!{className}ByQuery($!queryInstName));} catch(Exception e) {result.setSuccess(false);}return result;  }public Result<$!className> get$!{className}ById($!pkSimpleType $!{pkid}) {Result<$!className> result = new Result<$!className>();try {          result.addDefaultModel($!{daoName}.select$!{className}ById($!{pkid}));} catch(Exception e) {result.setSuccess(false);}return result;  }public Result<List<$!className>> get$!{className}sByPage(Query$!className$!queryInstName) {Result<List<$!className>> result = new Result<List<$!className>>();$!pkSimpleType totalItem =$!{daoName}.countByQuery($!queryInstName);$!{queryInstName}.setTotalItem(totalItem);$!{queryInstName}.repaginate();if (totalItem > 0) {result.addDefaultModel($!{daoName}.select$!{className}ByPage($!queryInstName));} else {result.addDefaultModel(newArrayList<$!className>());}result.setTotalItem(totalItem);result.setPageSize($!{queryInstName}.getPageSize());result.setPage($!{queryInstName}.getPage());return result;}public Result<$!pkType> count(Query$!className $!queryInstName) {Result<$!pkType> result= new Result<$!pkType>();try { result.addDefaultModel($!{daoName}.countByQuery($!queryInstName));} catch(Exception e) {result.setSuccess(false);}return result;  }}

看起来咱们的代码生成器就要编写完成了,但是还有一点点问题,每中类别的代码生成入口在哪里?既然没有,那么就去编写一个就好了——针对每一种类别,编写一个即可,编写方式都大同小异。给你两个核心一点的就好了,剩下的自己去完善吧(至少domain dao mapper )。

package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;/*** 生成一些基本类,例如queryDO,resultDO等* @author pangzi**/
public class BaseGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {//由PaginateBaseDO.vm生成PaginateBaseDO.javathis.classType = QUERYDOMAIN;this.setTemplate("PaginateBaseDO.vm");this.setFileName("PaginateBaseDO.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain"));initClassMetaInfo();intitLocation();         this.generate();//由Result.vm生成Result.javathis.classType = DOMAIN;this.setTemplate("Result.vm");this.setFileName("Result.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base" );this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();//生成BaseDOthis.classType = DOMAIN;this.setTemplate("BaseDO.vm");this.setFileName("BaseDO.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base");this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();//生成输出文件格式this.classType = DOMAIN;this.setTemplate("NoNullFieldStringStyle.vm");this.setFileName("NoNullFieldStringStyle.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base" );this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();                }public Set<String> getResultImport() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)+ ".domain.base"  +".PaginateBaseDO" );context.put(IMPORTS,imports);return imports;}
}

package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;/*** 生成一些基本类,例如queryDO,resultDO等* @author pangzi**/
public class BaseGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {//由PaginateBaseDO.vm生成PaginateBaseDO.javathis.classType = QUERYDOMAIN;this.setTemplate("PaginateBaseDO.vm");this.setFileName("PaginateBaseDO.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain"));initClassMetaInfo();intitLocation();         this.generate();//由Result.vm生成Result.javathis.classType = DOMAIN;this.setTemplate("Result.vm");this.setFileName("Result.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base" );this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();//生成BaseDOthis.classType = DOMAIN;this.setTemplate("BaseDO.vm");this.setFileName("BaseDO.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base");this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();//生成输出文件格式this.classType = DOMAIN;this.setTemplate("NoNullFieldStringStyle.vm");this.setFileName("NoNullFieldStringStyle.java");this.setPackagee(prop.getProperty(GenerateEngine.PACKAGE_NAME)+ ".domain.base" );this.setImports(getResultImport());initClassMetaInfo();intitLocation();         this.generate();                }public Set<String>getResultImport() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)+ ".domain.base"  +".PaginateBaseDO" );context.put(IMPORTS,imports);return imports;}
}
/*** Copyright(c) 2004-2020 pangzi* com.pz.cg.gen.PojoCodeGenerator.java*/
package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;public class PojoGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName(this.className+ ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.domain") +this.getSecondDir());this.init(DOMAIN);context.put(IMPORTS,this.getPojoImports());this.generate();}protected Set<String>getPojoImports() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)  + ".domain.base.BaseDO");return imports;}
}
/****/
package com.pz.cg.gen;import java.util.HashSet;
import java.util.Set;import com.pz.cg.CodeGenerator;
import com.pz.cg.GenerateEngine;/*** @author pangzi**/
public class QueryPojoGenerator extends GenerateEngine implements CodeGenerator {public void generate(String tableName,String className) {this.tableName = tableName;this.className = className;this.setFileName("Query"+this.className + ".java");this.setPackagee(prop.getProperty(PACKAGE_NAME)+ "." + prop.getProperty("package.querydomain") +  this.getSecondDir());this.setImports(getImport());this.init(QUERYDOMAIN);context.put(IMPORTS,this.getImports());this.generate();}public Set<String> getImport() {Set<String> imports =new HashSet<String>();imports.add(prop.getProperty(PACKAGE_NAME)+ ".domain.base.PaginateBaseDO" );context.put(IMPORTS,imports);return imports;}
}

嗯,最后,咱们的生成器当然是使用maven的方式编写的啦,不看到最后,不想坚持的人,自然是得不到好东西的。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.pz</groupId><artifactId>pz-cg</artifactId><version>0.0.1-SNAPSHOT</version><dependencies><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.8.3</version></dependency><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>1.3.2</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.5</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.5</version></dependency><dependency><groupId>velocity-tools</groupId><artifactId>velocity-tools</artifactId><version>1.2</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>1.6.3</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.15</version><exclusions><exclusion><groupId>javax.jms</groupId><artifactId>jms</artifactId></exclusion><exclusion><groupId>com.sun.jdmk</groupId><artifactId>jmxtools</artifactId></exclusion><exclusion><groupId>com.sun.jmx</groupId><artifactId>jmxri</artifactId></exclusion></exclusions></dependency>       <dependency><groupId>org.springframework</groupId><artifactId>spring</artifactId><version>2.5.6</version></dependency>       </dependencies>
</project>

猿人君并没有把完整的东西放出来,因为猿人君是真心希望你认真去思考,完善一些东西,剩下的已经不多了,service的生成器并没有什么特别,只是希望你自己稍微完善下即可,如果实在想不出来,可以加猿人君的微信shangaladepangzi,暗号:代码生成获取。

手把手教你拥有自己的代码生成器-------->坑居多相关推荐

  1. 【2022最新】手把手教你拥有自己的服务器与网站(无需备案)

    手把手教你如何拥有服务器及网站,此文章适用于想拥有自己服务器做网站的小白. 前言 手把手教你如何拥有服务器及网站,此文章适用于想拥有自己服务器做网站的小白. 要求有一定的编程基础,成本大概花30元就可 ...

  2. 服务器系统2022安装wsl2,手把手教你踩坑:老白的Docker for Windows安装初探WSL 2 backend...

    手把手教你踩坑:老白的Docker for Windows安装初探WSL 2 backend 2020-06-16 13:29:47 15点赞 63收藏 4评论 创作立场声明:老白的踩坑记录 嗨,大家 ...

  3. 【填坑之旅】手把手教你如何用AndroidStudio上传自己的library到Jcenter库

    [填坑之旅]手把手教你如何用AndroidStudio上传自己的library到Jcenter库 前言:我们在使用AndroidStudio的时候,会经常用到第三方的代码库.引入第三方库的方式无非就是 ...

  4. 报名 | NVIDIA线下交流会:手把手教你搭建TensorFlow Caffe深度学习服务器

    7月21日(周六)下午14:30,量子位与NVIDIA英伟达开发者社区联合举办线下交流会,拥有丰富一线开发经验的NVIDIA开发者社区经理Ken He,将手把手教你搭建TensorFlow & ...

  5. 手把手教你用Vscode Debugger调试代码

    http://shooterblog.site/2018/05/19/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E7%94%A8Vscode%20De ...

  6. 华为表哥手把手教你利用Jenkins持续集成iOS项目,教不会我花式拉翔!!!

    手把手教你利用Jenkins持续集成iOS项目: 前言 众所周知,现在App的竞争已经到了用户体验为王,质量为上的白热化阶段.用户们都是很挑剔的.如果一个公司的推广团队好不容易砸了重金推广了一个APP ...

  7. 自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)

    本文由作者FreddyChen原创分享,为了更好的体现文章价值,引用时有少许改动,感谢原作者. 1.写在前面 一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间.今天终于从公司离职了, ...

  8. qml 不刷新 放大还原_耳放攻略2020版|提升耳机音质,怎么买才不坑?手把手教你买...

    耳放攻略2020白皮书:提升耳机音质,怎么买才不坑?手把手教你买 每次都会给朋友们推荐一些市场上热门的耳放型号或者我们使用过觉得声价比不错的型号.但对于更多的人来说,可能在使用习惯上或者使用的场景上并 ...

  9. 【保姆级教程】手把手教你用github制作学术个人主页(学者必备)

    很多朋友到了本科生或者研究生或者博士生的高年级,有了制作个人主页的需求,今天这一期博客将以academicpages模板为例,手把手教你快速制作一个简洁能用的个人主页. 1.首先你需要拥有一个gith ...

最新文章

  1. 新型内存攻击,专治制程提高的芯片
  2. Android端打开HttpDns的正确姿势
  3. List与Set接口的方法
  4. LeetCode 664. 奇怪的打印机(区间DP)
  5. 评测任务征集 | 全国知识图谱与语义计算大会(CCKS 2022)
  6. net start mysql 发生系统错误 5。 拒绝访问。的解决方法
  7. 医疗机构被勒索软件攻击的可能性是金融机构的114倍
  8. python导入数据画折线图_Python读取Excel表格,并同时画折线图和柱状图的方法
  9. 证件照怎么换底色?不用ps,Word就能一键更换红白蓝底色
  10. win10如何设置保护色保护眼睛
  11. 面经手册 · 第12篇《面试官,ThreadLocal 你要这么问,我就挂了!》
  12. 基于ICP算法计算点集之间的变换矩阵(旋转、平移)
  13. 5个免费的AI智能抠图工具,一键去除图片背景!
  14. 一、Java面试基础之面向对象的特征:继承、封装和多态(原创实例)
  15. Android开发:隐藏和显示底部导航栏
  16. 美女主播即将失业?韩国首个AI女主播“金柱夏”诞生
  17. python 电动车问题
  18. 创业如何募集资金?哪些类型的创业比较赚钱
  19. proteus实现电子钟计时
  20. 十分钟搞懂Lombok使用与原理

热门文章

  1. 评估 OKRs 的最佳频率是什么?
  2. Android 框架学习2:源码分析 EventBus 3.0 如何实现事件总线
  3. Tecnomatix plant simulation 进阶--生产系统仿真:Plant Simulation应用教程
  4. 《操作系统》-生产者消费者问题
  5. 应用层加密方_加密应用层数据之前要问的6个问题
  6. Mysql探索之索引详解,又能和面试官互扯了~,java分布式系统面试题
  7. scratch字母点头问好 电子学会图形化编程scratch等级考试一级真题和答案2020-9
  8. 参加孤尽老师DIY班一期的主要收获
  9. 【陈工笔记】# 如何用WPS演示给PPT加水印 #
  10. QingCloud首届用户大会亮点抢先看