哈哈,猿设计终于讲完了,接下来的一段时间,工厂君打算和大家一起来实现我们之间的设计——构建一个自己的电商系统来玩耍。嗯,这个可是一个大工程,代码量很大,业务也比较复杂,要搞就好好搞,代码方面还是需要一些规范的。
在这个背景下,工厂君为了解决代码的编写速度,也差点被逼上梁山——一个人的力量实在太有限了。工厂君灵机一动,决定搞一个适合项目开发的利器出来——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 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> 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 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> getMallBrandsByQuery(QueryMallBrand queryMallBrand);
/**
* 通过主键id查询MallBrand
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getModel()得到查询的单条mallBrand信息
* @param id
* @return
*/
public Result getMallBrandById(long id);
/**
* 查询列表,包含分页查询
* 查询分页信息,请设置
* QueryMallBrand.setIndex(设置当前页数)
* QueryMallBrand.setPageSize(设置当前页面数据行数)
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getTotal()返回结果总数
* 通过result.getModel()得到查询的单页列表信息
* @param queryMallBrand
* @return
*/
public Result> getMallBrandsByPage(QueryMallBrand queryMallBrand);
/**
* 查询总数
* @param queryMallBrand
* @return
*/
public Resultcount(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 ResultaddMallBrand(MallBrand mallBrand) {
Resultresult = new Result();
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>getMallBrandsByQuery(QueryMallBrand queryMallBrand) {
Result>result = new Result>();
try {
queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
result.addDefaultModel("MallBrands",mallBrandDao.selectMallBrandByQuery(queryMallBrand));
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public ResultgetMallBrandById(long id) {
Resultresult = new Result();
try {
result.addDefaultModel("MallBrand",mallBrandDao.selectMallBrandById(id));
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result>getMallBrandsByPage(QueryMallBrand queryMallBrand) {
Result>result = new Result>();
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());
}
result.setTotalItem(totalItem);
result.setPageSize(queryMallBrand.getPageSize());
result.setPage(queryMallBrand.getPage());
return result;
}
public Resultcount(QueryMallBrand queryMallBrand) {
Result result =new Result();
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
*/
ListselectMallBrandByQuery(QueryMallBrand query);
/**
* 根据查询条件返回列表
* @param QueryMallBrand query
* @return
*/
List selectMallBrandByPage(QueryMallBrandquery);
/**
* 根据ID查询对象
* @param Long id
* @return
*/
MallBrand selectMallBrandById(long id);
/**
* 根据id修改记录 注意:有值的字段才更新
* @param MallBrand record
* @return
*/
int updateMallBrandByIdModified(MallBrandrecord);
}
Mapper文件:
id,
brand_name,
logo,
first_char,
status,
active,
create_user,
modify_user,
created,
modified
1=1
and id = #{id}
and brand_name = #{brandName}
and logo = #{logo}
and first_char = #{firstChar}
and status = #{status}
and active = #{active}
and create_user = #{createUser}
and modify_user = #{modifyUser}
and created = #{created}
and modified = #{modified}
delete from mall_brand
where id = #{id}
delete from mall_brand
INSERT INTO
mall_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})
SELECT @@IDENTITY AS ID
insert into mall_brand
id,
brand_name,
logo,
first_char,
status,
active,
create_user,
modify_user,
created,
modified,
#{id},
#{brandName},
#{logo},
#{firstChar},
#{status},
#{active},
#{createUser},
#{modifyUser},
now(),
now(),
SELECT @@IDENTITY AS ID
update mall_brand
brand_name = #{brandName},
logo = #{logo},
first_char = #{firstChar},
status = #{status},
active = #{active},
create_user = #{createUser},
modify_user = #{modifyUser},
created = #{created},
modified=now(),
where id = #{id}
以上就是品牌管理功能的基本代码,看上去,这些代码确实很多,而且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 MapgetTableColumns(Connection con, String tableName) {
Mapcolumns = new HashMap();
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.Driver
jdbc.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/sqlmap
service.out.path=D:/workspace-pangzi/pangzi-service/src/main/java
action.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 context = null;
protected static Map 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 imports = newHashSet();
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();
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();
List fields =new ArrayList();
Connection conn = null;
Statement st = null;
ResultSet rs = null;
Set ip = null;
Field pkField = new Field();
String pkId = "";
String pkName = "";
String pkType = "";
String pkSimpleType ="";
Mapcomment = new HashMap();
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();
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 SetgetImports() {
return imports;
}
protected voidsetImports(Set 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.java
this.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.java
this.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();
//生成BaseDO
this.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 SetgetResultImport() {
Set imports =new HashSet();
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 SetgetPojoImports() {
Set imports =new HashSet();
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 getImport() {
Set imports =new HashSet();
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 getDaoImport(){
Set imports =new HashSet();
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 SetgetDaoImplImport() {
Set imports =new HashSet();
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 SetgetServiceImport() {
Set imports =new HashSet();
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 SetgetServiceImplImport() {
Set imports =new HashSet();
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 的package
context.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中
*/
@Override
public 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;
#end
public $!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")
#foreach($field in $fields)
#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
#set($index=1)
#foreach($field in $fields)
#if($index==$fields.size())
$!{field.fieldName}
#else
$!{field.fieldName},
#end
#set($index =$index+1)
#end
1=1
#foreach($field in $fields)
and $!field.fieldName = #{$!{field.propertyName}}
#end
select
from $!{tableName}
LIMIT #{startRow},#{pageSize}
select
from $!{tableName}
where $!pkFiled.fieldName =#{$!pkFiled.propertyName}
delete from $!{tableName}
where $!pkFiled.fieldName =#{$!pkFiled.propertyName}
delete from $!{tableName}
INSERT INTO
$!{tableName}($!insertFields)
VALUES($!insertSql)
SELECT @@IDENTITY AS ID
insert into $!{tableName}
#foreach($field in $fields)
#if($!field.propertyName=='created'||$!field.propertyName=='modified')
$!{field.fieldName},
#else
$!{field.fieldName},
#end
#end
#foreach($field in $fields)
#if($!field.propertyName=='created'||$!field.propertyName=='modified')
now(),
#else
#{$!{field.propertyName}},
#end
#end
SELECT @@IDENTITY AS ID
update $!{tableName}
#foreach($field in $fields)
#if($!field.fieldName!=$!pkFiled.fieldName)
#if($!field.propertyName=='modified')
$!field.fieldName=now(),
#else
$!field.fieldName = #{$!{field.propertyName}},
#end
#end
#end
where $!pkFiled.fieldName =#{$!pkFiled.propertyName}
以上的模板,基本上解决了实际开发的需要。由于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 extends PaginateBaseDO implements Serializable {
/**
*
*/
private static final longserialVersionUID = 6028636097083630372L;
/**
* 是否成功
*/
private boolean success = true;
/**
* service返回的对象
*/
private Mapresult = new HashMap();
/**
* 默认的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 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> 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> 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};
#end
public 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> get$!{className}sByQuery(Query$!className$!queryInstName) {
Result> result = new Result>();
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> get$!{className}sByPage(Query$!className$!queryInstName) {
Result> result = new Result>();
$!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.java
this.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.java
this.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();
//生成BaseDO
this.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 getResultImport() {
Set imports =new HashSet();
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.java
this.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.java
this.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();
//生成BaseDO
this.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 SetgetResultImport() {
Set imports =new HashSet();
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 SetgetPojoImports() {
Set imports =new HashSet();
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 getImport() {
Set imports =new HashSet();
imports.add(prop.getProperty(PACKAGE_NAME)+ ".domain.base.PaginateBaseDO" );
context.put(IMPORTS,imports);
return imports;
}
}
嗯,最后,咱们的生成器当然是使用maven的方式编写的啦,不看到最后,不想坚持的人,自然是得不到好东西的。
4.0.0
com.pz
pz-cg
0.0.1-SNAPSHOT
commons-beanutils
commons-beanutils
1.8.3
commons-collections
commons-collections
3.2.1
commons-io
commons-io
1.3.2
commons-lang
commons-lang
2.5
commons-logging
commons-logging
1.1.1
mysql
mysql-connector-java
5.1.5
velocity-tools
velocity-tools
1.2
org.apache.velocity
velocity
1.6.3
log4j
log4j
1.2.15
javax.jms
jms
com.sun.jdmk
jmxtools
com.sun.jmx
jmxri
org.springframework
spring
2.5.6
猿人君并没有把完整的东西放出来,因为猿人君是真心希望你认真去思考,完善一些东西,剩下的已经不多了,service的生成器并没有什么特别,只是希望你自己稍微完善下即可,如果实在想不出来,可以在blog中寻找我的联系方式。