有感於馬上要做個比較大的業務系統,想到那無止境的增刪改查、粘貼復制,頓時腦后升起一陣涼風。於是想到個找或者寫一個Java代碼的生成器,這樣在正常開發進度下,也能余下更多的時間去做些別的事情。
閑話少說,首先總結下需求:
我需要的這個工具能夠讀取數據庫表結構,通過對字段類型、名稱等分析得到需要的各種變量,根據模板生成相應的pojo類、hibernate的xml配置文件、dao和service的接口和類。
需求看起來很簡單,但是一是沒搞過這樣的小工具,二是技術不過關,所以還是想到了找找有沒有開源的代碼拿過來根據自己的需求改。
於是找到了rapid-generator這個開源工具,同學們可以下載rapid-generator直接使用,根據自己的需求寫好模板就行了。
由於自己項目中的特殊情況和公司規范等因素,另外也是想學習下別人的設計,所以我對源碼進行了刪減和功能的修改。
來看下主要的類:
Table:根據表結構建立的對象。
Column:根據表中每列建立的對象。
Generator:生成器核心類,主要負責根據表對象和讀取FreeMarker模板生成最后的java代碼文件。
GeneratorControl:控制生成過程等的一些參數,例如文件是否覆蓋、文件編碼等。
GeneratorProperties:讀取配置文件的類,配置文件包括數據庫連接信息和一些基本的參數配置。
下面來看一下模板的編寫:
pojo模板:
<#include "/java_copyright.include">
<#assign className = table.className>
<#assign classNameLower = className?uncap_first>
package ${basepackage}.pojo.${mpackage}.${table.classNameFirstLower};
<#include "/java_imports.include">
import com.linkage.agri.pojo.base.BaseEntity;
public class ${className} extends BaseEntity {
private static final long serialVersionUID = 5454155825314635342L;
<#list table.columns as column>
/**
* ${column.remarks}
*/
private ${column.simpleJavaType} ${column.columnNameLower};
</#list>
<@generateJavaColumns/>
<#macro generateJavaColumns>
<#list table.columns as column>
<#if column.isDateTimeColumn>
public String get${column.columnName}String() {
return DateConvertUtils.format(get${column.columnName}(), FORMAT_${column.constantName});
}
public void set${column.columnName}String(String ${column.columnNameLower}) {
set${column.columnName}(DateConvertUtils.parse(${column.columnNameLower}, FORMAT_${column.constantName},${column.simpleJavaType}.class));
}
</#if>
public void set${column.columnName}(${column.simpleJavaType} ${column.columnNameLower}) {
this.${column.columnNameLower} = ${column.columnNameLower};
}
public ${column.simpleJavaType} get${column.columnName}() {
return this.${column.columnNameLower};
}
</#list>
</#macro>
FreeMarker的基本語法可以看下FreeMarker中文手冊。
${}可以引用很多變量,這些變量包括:環境變量、table對象、配置變量等,這些變量都裝在一個map里,如果自己有特殊需求,當然可以修改源碼來裝載更多的變量值。
注意:當變量為對象時,訪問對象的屬性是通過get方法來訪問的。例如${table.classNameFirstLower},是引用table對象的getNameFirstLower()方法,即使table對象中沒有nameFirstLower這個屬性也能夠引用。
再看下我寫的dao的模板:
<#include "/java_copyright.include">
<#assign className = table.className>
<#assign classNameLower = className?uncap_first>
package ${basepackage}.dao.${mpackage}.${table.classNameFirstLower};
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import com.linkage.agri.dao.base.AbstractHibernateDAO;
import com.linkage.agri.exception.DAOException;
import ${basepackage}.pojo.${mpackage}.${classNameLower}.${className};
<#include "/java_imports.include">
public Class ${className}DAOImpl extends AbstractHibernateDAO implements ${className}DAO
{
/**
* <query all>
* @param paramMap
* @param orderMap
* @param pageNum
* @param pageSize
* @return
* @throws DAOException
*/
@SuppressWarnings
public List<${className}> queryList${className}ByAttr(Map<String, Object> paramMap, Map<String, String> orderMap, int pageNum,
int pageSize)
throws DAOException
{
return super.listInstances(${className}.class, paramMap, orderMap, pageNum, pageSize);
}
/**
*
* <find one by id>
* @param serial
* @throws DAOException
*/
public ${className} find${className}By${table.pkColumn.columnName}(${table.pkColumn.simpleJavaType} ${table.pkColumn.columnNameFirstLower})
throws DAOException
{
return (${className})super.findById(${className}.class, ${table.pkColumn.columnNameFirstLower});
}
/**
*
* <save one>
* @param ${table.classNameFirstLower}
* @throws DAOException
*/
public ${table.pkColumn.simpleJavaType} save${className}(${className} ${table.classNameFirstLower})
throws DAOException
{
return (${table.pkColumn.simpleJavaType})super.saveInstance(${table.classNameFirstLower});
}
/**
*
* <update one>
* @param ${table.classNameFirstLower}
* @throws DAOException
*/
public void update${className}(${className} ${table.classNameFirstLower})
throws DAOException
{
super.updateInstance(${className});
}
/**
* <check one is have?>
* @param paramMap
* @return
* @throws DAOException
*/
public boolean check${className}IsHaveByAttr(Map<String, Object> paramMap)
throws DAOException
{
StringBuffer sqlBuffer = new StringBuffer();
sqlBuffer.append("SELECT COUNT(*) FROM ${table.sqlName} T ");
sqlBuffer.append("WHERE T.${table.pkColumn.sqlName} = ? ");
BigDecimal big = (BigDecimal)super.findUniqueResultBySQLWithParams(sqlBuffer.toString(), paramMap.get("${table.pkColumn.columnNameFirstLower}"));
return big.intValue() > 0 ? false : true;
}
/**
* <update some>
* @param ${table.classNameFirstLower}List
* @return
* @throws DAOException
*/
public void update${className}Batch(List<${className}> ${table.classNameFirstLower}List)
throws DAOException
{
super.updateBatchInstance(${table.classNameFirstLower}List);
}
/**
*
* <delete one>
* @param ${table.classNameFirstLower}
* @throws DAOException
*/
public void delete${className}(${className} ${table.classNameFirstLower})
throws DAOException
{
super.deleteInstance(${table.classNameFirstLower});
}
}
建立模板是能否解決自己問題的關鍵,在摸索出門道以后寫模板變的十分簡單。其實原理很簡單,就是用一系列占位符來替換實際的變量值。
模板路徑可以按照實際項目中的路徑來拜訪,生成器可以讀取某個路徑下所有模板進行生成,結果如下圖:
接口和實現:
就說這么多吧,感興趣的可以研究下這個工具的源碼,也DIY出一套自己代碼生成工具。不感興趣的直接寫模板就能用。