【1.你覺得我會眼睜睜的看着你去送死?我會閉着眼睛。2.給你講個故事,從前有個笨蛋,他很是笨,別人問他問題他只會回答「沒有」,這個故事你聽過嗎?】html
你們好,我錄製的視頻《Java之優雅編程之道》已經在CSDN學院發佈了,有興趣的同窗能夠購買觀看,相信你們必定會收穫到不少知識的。謝謝你們的支持……java
視頻地址:http://edu.csdn.net/lecturer/994web
如何利用 Java + Freemarker 實現代碼生成器???spring
作業務開發的時候,常常要根據創建好的數據庫表,生成相關的 Model , DTO , Service, Controller , DAO 等等。包括基本的增刪改查。而這些細活每每比較簡單且沒有挑戰性,純粹苦力活。所以,根據公司的框架,開發一個代碼生成器是頗有必要的。sql
這裏:
假如你有必定的java基礎;
假如你熟悉freemarker模板引擎;
假如你熟悉MVC框架;
假如你熟悉Spring Data 框架數據庫
因爲代碼生成器是要生成不少文件的,包 Test.java , TestDTO.java ,TestController.java , TestServiceImpl.java , ITestService.java , TestDAO.java 等等這些文件。全部考慮用 freemarker 強大的模板引擎,製做相關的模板。apache
咱們首先要獲取數據庫的鏈接,這裏我只貼出相關的代碼。CREATE TABLE "public"."ay_test" ( "id" varchar(32) COLLATE "default" NOT NULL, "name" varchar(10) COLLATE "default", "birth_date" timestamp(6), "remark" text COLLATE "default", CONSTRAINT "ay_test_pkey" PRIMARY KEY ("id") )
private final String URL = "jdbc:postgresql://192.168.3.160:10655/cibpm"; private final String USER = "postgres"; private final String PASSWORD = "888888"; private final String DRIVER = "org.postgresql.Driver"; public Connection getConnection() throws Exception{ Class.forName(DRIVER); Connection connection= DriverManager.getConnection(URL, USER, PASSWORD); return connection; }
private final String changeTableName = replaceUnderLineAndUpperCase(tableName); Connection connection = getConnection(); DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet resultSet = databaseMetaData.getColumns(null,"%", tableName,"%");
package com.evada.inno.pm.code.generate.util; import com.evada.inno.pm.code.generate.model.ColumnClass; import freemarker.template.Template; import org.apache.commons.lang3.StringUtils; import java.io.*; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 描述:代碼生成器 * Created by Ay on 2017/5/1. */ public class CodeGenerateUtils { private final String AUTHOR = "Ay"; private final String CURRENT_DATE = "2017/05/03"; private final String tableName = "tm_project_quality_problem"; private final String packageName = "com.evada.pm.process.manage"; private final String tableAnnotation = "質量問題"; private final String URL = "jdbc:postgresql://192.168.3.160:10655/cibpm"; private final String USER = "postgres"; private final String PASSWORD = "888888"; private final String DRIVER = "org.postgresql.Driver"; private final String diskPath = "D://"; private final String changeTableName = replaceUnderLineAndUpperCase(tableName); public Connection getConnection() throws Exception{ Class.forName(DRIVER); Connection connection= DriverManager.getConnection(URL, USER, PASSWORD); return connection; } public static void main(String[] args) throws Exception{ CodeGenerateUtils codeGenerateUtils = new CodeGenerateUtils(); codeGenerateUtils.generate(); } public void generate() throws Exception{ try { Connection connection = getConnection(); DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet resultSet = databaseMetaData.getColumns(null,"%", tableName,"%"); //生成Mapper文件 generateMapperFile(resultSet); //生成Dao文件 generateDaoFile(resultSet); //生成Repository文件 generateRepositoryFile(resultSet); //生成服務層接口文件 generateServiceInterfaceFile(resultSet); //生成服務實現層文件 generateServiceImplFile(resultSet); //生成Controller層文件 generateControllerFile(resultSet); //生成DTO文件 generateDTOFile(resultSet); //生成Model文件 generateModelFile(resultSet); } catch (Exception e) { throw new RuntimeException(e); }finally{ } } private void generateModelFile(ResultSet resultSet) throws Exception{ final String suffix = ".java"; final String path = diskPath + changeTableName + suffix; final String templateName = "Model.ftl"; File mapperFile = new File(path); List<ColumnClass> columnClassList = new ArrayList<>(); ColumnClass columnClass = null; while(resultSet.next()){ //id字段略過 if(resultSet.getString("COLUMN_NAME").equals("id")) continue; columnClass = new ColumnClass(); //獲取字段名稱 columnClass.setColumnName(resultSet.getString("COLUMN_NAME")); //獲取字段類型 columnClass.setColumnType(resultSet.getString("TYPE_NAME")); //轉換字段名稱,如 sys_name 變成 SysName columnClass.setChangeColumnName(replaceUnderLineAndUpperCase(resultSet.getString("COLUMN_NAME"))); //字段在數據庫的註釋 columnClass.setColumnComment(resultSet.getString("REMARKS")); columnClassList.add(columnClass); } Map<String,Object> dataMap = new HashMap<>(); dataMap.put("model_column",columnClassList); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateDTOFile(ResultSet resultSet) throws Exception{ final String suffix = "DTO.java"; final String path = "D://" + changeTableName + suffix; final String templateName = "DTO.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateControllerFile(ResultSet resultSet) throws Exception{ final String suffix = "Controller.java"; final String path = diskPath + changeTableName + suffix; final String templateName = "Controller.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateServiceImplFile(ResultSet resultSet) throws Exception{ final String suffix = "ServiceImpl.java"; final String path = diskPath + changeTableName + suffix; final String templateName = "ServiceImpl.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateServiceInterfaceFile(ResultSet resultSet) throws Exception{ final String prefix = "I"; final String suffix = "Service.java"; final String path = diskPath + prefix + changeTableName + suffix; final String templateName = "ServiceInterface.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateRepositoryFile(ResultSet resultSet) throws Exception{ final String suffix = "Repository.java"; final String path = diskPath + changeTableName + suffix; final String templateName = "Repository.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateDaoFile(ResultSet resultSet) throws Exception{ final String suffix = "DAO.java"; final String path = diskPath + changeTableName + suffix; final String templateName = "DAO.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateMapperFile(ResultSet resultSet) throws Exception{ final String suffix = "Mapper.xml"; final String path = diskPath + changeTableName + suffix; final String templateName = "Mapper.ftl"; File mapperFile = new File(path); Map<String,Object> dataMap = new HashMap<>(); generateFileByTemplate(templateName,mapperFile,dataMap); } private void generateFileByTemplate(final String templateName,File file,Map<String,Object> dataMap) throws Exception{ Template template = FreeMarkerTemplateUtils.getTemplate(templateName); FileOutputStream fos = new FileOutputStream(file); dataMap.put("table_name_small",tableName); dataMap.put("table_name",changeTableName); dataMap.put("author",AUTHOR); dataMap.put("date",CURRENT_DATE); dataMap.put("package_name",packageName); dataMap.put("table_annotation",tableAnnotation); Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"),10240); template.process(dataMap,out); } public String replaceUnderLineAndUpperCase(String str){ StringBuffer sb = new StringBuffer(); sb.append(str); int count = sb.indexOf("_"); while(count!=0){ int num = sb.indexOf("_",count); count = num + 1; if(num != -1){ char ss = sb.charAt(count); char ia = (char) (ss - 32); sb.replace(count , count + 1,ia + ""); } } String result = sb.toString().replaceAll("_",""); return StringUtils.capitalize(result); } }
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 用來封裝 數據庫表元數據的信息,如字段名稱,字段類型,字段註釋等等。api
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結尾的。markdown
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代碼生成器的簡單實現
若是有帶給你一絲絲小快樂,就讓快樂繼續傳遞下去,歡迎點贊、頂、歡迎留下寶貴的意見、多謝支持!