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

在这个背景下,工厂君为了解决代码的编写速度,也差点被逼上梁山——一个人的力量实在太有限了。工厂君灵机一动,决定搞一个适合项目开发的利器出来——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字段的映射,我们需要先对字段进行抽象,那么字段这个类自然会具有以下几个属性。


/*** 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

考虑到Controller层在实际的应用场景种,会需要编写页面逻辑以及很多后端验证性的代码,用代码生成的方式去编写,意义不是很大。对于相对简单的系统而言,manager层存在的意义不是很大,所以我们重点需要的是domin,query,dao,service,以及重中之重的mapper文件。

架子已经搭好了,我们的重头戏来了,读取配置文件,然后生成对应的代码的事情需要开始了。


/*** 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的生成器并没有什么特别,只是希望你自己稍微完善下即可,如果实在想不出来,可以在blog中寻找我的联系方式。

手把手教你开发属于自己的代码生成器相关推荐

  1. java 注册探探账号_零基础手把手教你开发探探类社交软件Tinder

    原标题:零基础手把手教你开发探探类社交软件Tinder 目录介绍 1.关于项目App整体架构 1.1项目整体架构 1.1.1 目前项目使用架构 1.1.2 目前常见的架构 1.1.3 MVP架构优点及 ...

  2. 手把手教你开发photoshop面板插件(附demo和工具)

    手把手教你开发photoshop面板插件(附demo和工具) 一.前言 二.插件演示 三.目录文件介绍 3.1 插件安装 3.2 开启ps开发模式 3.3 插件文件介绍 3.4 manifest 文件 ...

  3. 解放前端工程师——手把手教你开发自己的自定义列表和自定义表单系列之二接口

    前言 阅读前请按照顺序参看系列文章,效果更佳! Vue中路由到一个公共组件,然后根据路径中是否存在文件动态加载组件 解放前端工程师--手把手教你开发自己的自定义列表和自定义表单系列之一缘起 据说系列文 ...

  4. 手把手教你开发IOT设备

    手把手教你开发IOT设备 1.概述 IOT设备的开发是基于rt-thread rtos实现.rt-thread是一个国产RTOS,它是一个RTOS,但又不仅仅是RTOS,支持大量的芯片,驱动,还包含大 ...

  5. 手把手教你开发Pro/TOOLKIT应用程序(一)

    前言 本教程采用VS2008 + Pro/E Wildfire5.0来讲解怎样开发Pro/TOOLKIT应用程序. 开发Pro/TOOLKIT应用程序时,Pro/E和Visual Studio的版本需 ...

  6. 手把手教你开发chrome扩展一:开发Chrome Extenstion其实很简单

    手把手教你开发chrome扩展一:开发Chrome Extenstion其实很简单 参考地址为:手把手教你开发chrome扩展一:开发Chrome Extenstion其实很简单

  7. 物联网全栈教程--手把手教你开发一个智能浇花器

    下面来说一下课程的安排,本教程可以分为三大章节,30个小章节,在1-10章节,手把手讲解了如何实现一个定时/实时控制的浇花器,可以对浇花器电量进行显示,可以进行定时设置,以及实时控制,低功耗模式等等, ...

  8. 手把手教你开发enc424j600+Lwip以太网-送全部源码

    每个单片机爱好者以及嵌入式软件开发人员,都希望能够将设计的产品连入网络,在众多的网络通讯方式中,以太网是最经典,跨度最广的一种方式,它具备带宽大[100Mbps~1000Mbps],延时小[局域网达到 ...

  9. 手把手教你开发微信小程序中的插件

    继上次 手把手教你实现微信小程序中的自定义组件 已经有一段时间了(不了解的小伙伴建议去看看,因为插件很多内容跟组件相似),今年3月13日,小程序新增了 小程序**「插件」 功能,以及开发者工具新增 「 ...

最新文章

  1. spark+openfire即时通讯工具二次开发参考文档
  2. 单工 半双工 全双工
  3. SAP UI5 CreateBindingContext 方法的实现逻辑
  4. 网站开发用什么语言好_网站开发教程:企业如何用网站开启在线业务?
  5. Flask 第三方组件之 WTForms
  6. 算法分析之对问题建模
  7. 第三方依赖关系的风险:利用数十个易受攻击的 NuGet包瞄准 .NET 平台
  8. 从外行到外包,从手工测试到知名互联大厂测开 我经历了我这个年龄段不该经历的事情...
  9. 【转】彻底删除打印机
  10. API RSA签名颁发证书
  11. 支持机顶盒的电影服务器,手把手教你用网络机顶盒看电影
  12. ABT 共识社区北京聚会 共建去中心化生态 | ArcBlock 社区
  13. 无法打开文件“d3dx9.lib_三个小技巧,教会你解决无法打开手机文件问题
  14. C++ 模板类与头文件
  15. Wpa_supplicant.conf用法小结
  16. python语言的注释语句引导符不包括什么_以下选项中,哪一个是Python语言中代码注释使用的符号?________...
  17. Tether操纵市场了吗?
  18. 透明导航 html,导航栏(Nav) 全透明效果的一种另类实现
  19. Git-回退到指定版本
  20. 智能家居软文营销以受众为中心,创作有说服力的文案

热门文章

  1. 【企业工程实践】云服务牛刀小试_DayFour
  2. Python播放MP3音频文件
  3. 新一代经济型加密狗——超级狗
  4. keil5 每次重启或者编译显示save changes to xxx.c
  5. MathType公式上浮该怎么办?
  6. 持续集成docker—第三篇(docker swarm集群搭建)
  7. 文章出轨被爆是愚人节整蛊 网民你知道吗?
  8. xtu oj 1319
  9. Vue实现简单——计数器
  10. 瑞丹斯VR电影节:解读VR,带来全新的观影体验