【1.你以为我会眼睁睁的看着你去送死?我会闭着眼睛。2.给你讲个故事,从前有个笨蛋,他非常笨,别人问他问题他只会回答“没有”,这个故事你听过吗?】
大家好,我录制的视频《Java之优雅编程之道》已经在学院发布了,有兴趣的同学可以购买观看,相信大家一定会收获到很多知识的。谢谢大家的支持……
视频地址:http://edu.csdn.net/lecturer/994
如何利用 Java + Freemarker 实现代码生成器???
做业务开发的时候,经常要根据建立好的数据库表,生成相关的 Model , DTO , Service, Controller , DAO 等等。包括基本的增删改查。而这些细活往往比较简单且没有挑战性,纯粹苦力活。因此,根据公司的框架,开发一个代码生成器是很有必要的。
这里: 假如你有一定的java基础; 假如你熟悉freemarker模板引擎; 假如你熟悉MVC框架; 假如你熟悉Spring Data 框架
由于代码生成器是要生成很多文件的,包 Test.java , TestDTO.java ,TestController.java , TestServiceImpl.java , ITestService.java , TestDAO.java 等等这些文件。所有考虑用 freemarker 强大的模板引擎,制作相关的模板。
FreeMarkerTemplateUtils工具类用来配置模板所在的路径
package com.evada.inno.pm.code.generate.util; import com.evada.inno.core.exception.BusinessException; import freemarker.cache.ClassTemplateLoader; import freemarker.cache.NullCacheStorage; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; import java.io.IOException; /** * Created by Ay on 2016/7/27. */ public class FreeMarkerTemplateUtils { private FreeMarkerTemplateUtils(){} private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22); static{ //这里比较重要,用来指定加载模板所在的路径 CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreeMarkerTemplateUtils.class, "/templates")); CONFIGURATION.setDefaultEncoding("UTF-8"); CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE); } public static Template getTemplate(String templateName) throws IOException { try { return CONFIGURATION.getTemplate(templateName); } catch (IOException e) { throw e; } } public static void clearCache() { CONFIGURATION.clearTemplateCache(); } }ColumnClass 用来封装 数据库表元数据的信息,如字段名称,字段类型,字段注释等等。
package com.evada.inno.pm.code.generate.model; /** * 数据库字段封装类 * Created by Ay on 2017/5/3. */ public class ColumnClass { /** 数据库字段名称 **/ private String columnName; /** 数据库字段类型 **/ private String columnType; /** 数据库字段首字母小写且去掉下划线字符串 **/ private String changeColumnName; /** 数据库字段注释 **/ private String columnComment; public String getColumnComment() { return columnComment; } public void setColumnComment(String columnComment) { this.columnComment = columnComment; } public String getColumnName() { return columnName; } public void setColumnName(String columnName) { this.columnName = columnName; } public String getColumnType() { return columnType; } public void setColumnType(String columnType) { this.columnType = columnType; } public String getChangeColumnName() { return changeColumnName; } public void setChangeColumnName(String changeColumnName) { this.changeColumnName = changeColumnName; } }Freemarker的模板文件,后缀都是以ftl结尾的。
Model.ftl可以生成字段的属性,并且可以生成字段属性对应的 set 和 get 方法,包括字段在数据库中对应的注释,以及一些该引入的包和类注释。
package ${package_name}.model; import com.evada.inno.common.domain.BaseModel; import com.evada.inno.common.listener.ICreateListenable; import com.evada.inno.common.listener.IDeleteListenable; import com.evada.inno.common.listener.IModifyListenable; import org.hibernate.annotations.Where; import javax.persistence.*; import java.util.Date; /** * 描述:${table_annotation}模型 * @author ${author} * @date ${date} */ @Entity @Table(name="${table_name_small}") @Where(clause = "status > '0'") @Inheritance(strategy= InheritanceType.SINGLE_TABLE) public class ${table_name} extends BaseModel implements ICreateListenable,IModifyListenable,IDeleteListenable { <#if model_column?exists> <#list model_column as model> /** *${model.columnComment!} */ <#if (model.columnType = 'varchar' || model.columnType = 'text')> @Column(name = "${model.columnName}",columnDefinition = "VARCHAR") private String ${model.changeColumnName?uncap_first}; </#if> <#if model.columnType = 'timestamp' > @Column(name = "${model.columnName}",columnDefinition = "TIMESTAMP") private Date ${model.changeColumnName?uncap_first}; </#if> </#list> </#if> <#if model_column?exists> <#list model_column as model> <#if (model.columnType = 'varchar' || model.columnType = 'text')> public String get${model.changeColumnName}() { return this.${model.changeColumnName?uncap_first}; } public void set${model.changeColumnName}(String ${model.changeColumnName?uncap_first}) { this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first}; } </#if> <#if model.columnType = 'timestamp' > public Date get${model.changeColumnName}() { return this.${model.changeColumnName?uncap_first}; } public void set${model.changeColumnName}(Date ${model.changeColumnName?uncap_first}) { this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first}; } </#if> </#list> </#if> }DTO.ftl 文件用来生产 DTO 值对象,该对象继承 Model.ftl 文件中的对象。
package ${package_name}.dto; import ${package_name}.model.${table_name}; /** * 描述:${table_annotation}DTO * @author ${author} * @date ${date} */ public class ${table_name}DTO extends ${table_name}{ }Service.ftl 模板用来生成服务层实现类,在模板中已经添加了增 ,删,改,查等方法,同时可以注入DAO和repository 到service中,一旦文件生成,就不需要我们去写了,很方便,提高开发的效率。
package ${package_name}.service.impl; import com.evada.inno.core.service.impl.BaseServiceImpl; import ${package_name}.model.${table_name}; import ${package_name}.repository.${table_name}Repository; import ${package_name}.service.I${table_name}Service; import ${package_name}.repository.mybatis.${table_name}DAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import ${package_name}.dto.${table_name}DTO; import org.apache.commons.beanutils.BeanUtils; import com.evada.inno.core.enums.StatusEnum; /** * 描述:${table_annotation} 服务实现层 * @author ${author} * @date ${date} */ @Service public class ${table_name}ServiceImpl extends BaseServiceImpl<${table_name}, String> implements I${table_name}Service { @Autowired private ${table_name}DAO ${table_name?uncap_first}DAO; @Autowired private ${table_name}Repository ${table_name?uncap_first}Repository; @Override public ${table_name}DTO findDTOById(String id) throws Exception { ${table_name}DTO ${table_name?uncap_first}DTO = ${table_name?uncap_first}DAO.findDTOById(id); return ${table_name?uncap_first}DTO; } @Override public ${table_name}DTO create${table_name}(${table_name}DTO ${table_name?uncap_first}DTO) throws Exception { ${table_name} ${table_name?uncap_first} = new ${table_name}(); BeanUtils.copyProperties(${table_name?uncap_first},${table_name?uncap_first}DTO); ${table_name?uncap_first}.setStatus(StatusEnum.ENABLE.toString()); ${table_name?uncap_first} = ${table_name?uncap_first}Repository.saveAndFlush(${table_name?uncap_first}); return this.findDTOById(${table_name?uncap_first}.getId()); } @Override public ${table_name}DTO update${table_name}(${table_name}DTO ${table_name?uncap_first}DTO)throws Exception { ${table_name} ${table_name?uncap_first} = new ${table_name}(); BeanUtils.copyProperties(${table_name?uncap_first},${table_name?uncap_first}DTO); ${table_name?uncap_first} = ${table_name?uncap_first}Repository.saveAndFlush(${table_name?uncap_first}); return this.findDTOById(${table_name?uncap_first}.getId()); }Interface.ftl 用来生成服务层接口,接口中定义了增,删,改,查等接口。
package ${package_name}.service; import com.evada.inno.core.service.IBaseService; import ${package_name}.model.${table_name}; import ${package_name}.dto.${table_name}DTO; /** * 描述:${table_annotation} 服务实现层接口 * @author ${author} * @date ${date} */ public interface I${table_name}Service extends IBaseService<${table_name},String> { /** * 描述:根据Id获取DTO * @param id */ ${table_name}DTO findDTOById(String id)throws Exception; ${table_name}DTO create${table_name}(${table_name}DTO ${table_name?uncap_first}DTO) throws Exception; void delete${table_name}(String id) throws Exception; ${table_name}DTO update${table_name}(${table_name}DTO ${table_name?uncap_first}DTO) throws Exception; }Respontory.ftl 用来生成 Repository 文件,这一块是 Spring Data的内容,可能不同的公司,使用的框架不一样。
package ${package_name}.repository; import com.evada.inno.core.repository.BaseJpaRepository; import ${package_name}.model.${table_name}; /** * 描述:${table_annotation} Repository接口 * @author ${author} * @date ${date} */ public interface ${table_name}Repository extends BaseJpaRepository<${table_name}, String> { }Mappter.ftl 用来生成 MyBatis 使用到的 mappter 文件,在文件中,定义了查询的sql语句。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="${package_name}.repository.mybatis.${table_name}DAO"> <resultMap id="${table_name}DTOResultMap" type="${package_name}.dto.${table_name}DTO"></resultMap> <sql id="findDtoSql"> select * from ( select * from ${table_name_small} temp ) t </sql> <select id="findDTOById" parameterType="String" resultMap="${table_name}DTOResultMap"> <include refid="findDtoSql"></include> <where> and t.id = ${r'#{id}'} </where> </select> <select id="find${table_name}Page" parameterType="${package_name}.dto.${table_name}DTO" resultMap="${table_name}DTOResultMap"> <include refid="findDtoSql" /> <where> </where> </select> </mapper>Controller.ftl 文件用来生成控制层类,类中已经帮我们生成了,增,删,改,查等路由。同时可以注入接口道控制层中。
package ${package_name}.controller; import com.evada.inno.core.annotation.Rest; import ${package_name}.service.I${table_name}Service; import ${package_name}.model.${table_name}; import ${package_name}.dto.${table_name}DTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.http.MediaType; import com.evada.inno.common.domain.ResultData; import com.evada.inno.core.util.AssertUtils; /** * 描述:${table_annotation}控制层 * @author ${author} * @date ${date} */ @Rest(${table_name}.class) public class ${table_name}Controller { @Autowired private I${table_name}Service ${table_name?uncap_first}Service; /** * 描述:根据Id 查询 * @param id ${table_annotation}id */ @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultData findById(@PathVariable("id") String id)throws Exception { ${table_name}DTO ${table_name?uncap_first}DTO = ${table_name?uncap_first}Service.findDTOById(id); AssertUtils.checkResourceFound(${table_name?uncap_first}DTO); return new ResultData(${table_name}DTO.class, ${table_name?uncap_first}DTO); } /** * 描述:创建${table_annotation} * @param ${table_name?uncap_first}DTO ${table_annotation}DTO */ @RequestMapping(value = "", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultData create(@RequestBody ${table_name}DTO ${table_name?uncap_first}DTO) throws Exception { return new ResultData(${table_name}.class,${table_name?uncap_first}Service.create${table_name}(${table_name?uncap_first}DTO)); } /** * 描述:删除${table_annotation} * @param id ${table_annotation}id */ @RequestMapping(value = "/{id}/bulk", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public void deleteById(@PathVariable("id") String id) throws Exception { ${table_name?uncap_first}Service.deleteById(id); } /** * 描述:更新${table_annotation} * @param id ${table_annotation}id */ @RequestMapping(value = "/{id}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultData update${table_name}(@PathVariable("id") String id,@RequestBody ${table_name}DTO ${table_name?uncap_first}DTO) throws Exception { ${table_name?uncap_first}DTO.setId(id); return new ResultData(${table_name}.class,${table_name?uncap_first}Service.update${table_name}(${table_name?uncap_first}DTO)); } }通过上面的代码生成,我们就可以把生成的文件复制到相关的目录,重新启动系统,这样,基本的增,删,改,查就实现了。
来自东野圭吾《信》
“大多数人都想置身于远离罪犯的地方。和犯罪者,特别是犯下抢劫杀人这样恶心犯罪的人,哪怕是间接的关系也不想有。因为稍微有点什么关系,没准也会被卷入莫名其妙的事情中去。排斥犯罪者或是与其近似的人,是非常正当的行为,也可以说是正当防卫的本能。”犯罪者必须要有这样的思想准备,就是自己犯罪的同时也抹杀了自己亲属在社会上的存在。所谓偏见,就是不平等看待,其产生的根源就在于人的自私本性。这里的不平等看待,其实可以视为一种处理各种社会关系时的态度平衡缺失,而总是向有利于自己的一方倾斜,除非不同的立场之间不存在厉害冲突。一般情况下,人都是首先从自己的角度来看问题,而不会力图站在他人的立场来考量,完全意义上的中立是不可能的,由己及人是必然的思维定势,偏见遂自然而生。【一只小青蛙厌倦了常年生活的小水沟——水沟的水越来越少,它已经没有什么食物了。小青蛙每天都不停地蹦,想要逃离这个地方。而它的同伴整日懒洋洋地蹲在浑浊的水洼里,说:“现在不是还饿不死 吗?你着什么急?”终于有一天,小青蛙纵身一跃,跳进了旁边的一个大河塘,那里面有很多好吃的,它可以自由游弋。小青蛙呱呱地呼唤自己的伙伴:“你快过来吧,这边简直是天堂!”但是它的同伴说:“我在这里已经习惯了,我从小就生活在这里,懒得动了!” 不久,水沟里的水干了,小青蛙的同伴活活饿死了。】
【1】Freemarker官网 【2】可用于企业级开发的JAVA代码生成器 【3】 一个java代码生成器的简单实现
如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎点赞、顶、欢迎留下宝贵的意见、多谢支持!