開發過程當中,經常建立了PO以後,還要去數據庫裏建立對應的表,類似的過程,卻要作兩次,有沒有感受很麻煩?使用Hibernate能夠生成表,還要去配置,並且沒法獲得表建立sql,因而 DBGenerator 應運而生了。DBGenerator是一個工具類,能獲取PO類的註解信息,自動生成表建立SQL,節省了不少時間,同時也能保證測試環境和生產環境表的一致性。java
commons-lang-2.6.jarsql
hibernate3.jar數據庫
package com.cg.db; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; import org.apache.commons.lang.StringUtils; import com.sun.xml.internal.bind.v2.model.core.ID; /** * * <p>根據PO生成表建立sql<p> * <p>根據read方法上的註解,生成數據庫字段信息,沒有read方法的Field跳過<p> * @since jdk1.6 * @date 2016-8-15 */ public class DBGenerator { /** * * 根據類的註解信息生成表sql * @param clazz * @return */ public String generate(Class clazz){ if(!clazz.isAnnotationPresent(Table.class)){ throw new RuntimeException("No Annotation[Table] founded in class["+clazz.getName()+"]"); } Table table = (Table)clazz.getAnnotation(Table.class); String tableName = table.name(); StringBuilder sb = new StringBuilder("-- Create table\n"); sb.append("create table " + tableName + "\n"); sb.append("(\n"); Field[] fieldArr = clazz.getDeclaredFields(); int maxLength = getMaxLength(clazz, fieldArr); String primaryKey = generateFields(clazz, sb, fieldArr, maxLength); sb.append(");\n"); generatePrimaryKey(sb, tableName, primaryKey); return sb.toString(); } /** * 生成sql字段 * @param clazz * @param sb * @param fieldArr * @param primaryKey * @param maxLength * @return */ private String generateFields(Class clazz, StringBuilder sb, Field[] fieldArr, int maxLength) { String primaryKey = ""; Field field; for(int i=0;i<fieldArr.length;i++){ field = fieldArr[i]; int offset; try { PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); Method mt = pd.getReadMethod(); if(null != mt){ Column col = (Column)mt.getAnnotation(Column.class); writeBlankSpace(sb, 2); sb.append(col.name()); offset = maxLength - col.name().length(); writeBlankSpace(sb, offset); writeBlankSpace(sb, 1); sb.append(convertJava2Oracle(field, col)); if(mt.isAnnotationPresent(Id.class)){ sb.append(" not null"); primaryKey = col.name(); } } } catch (IntrospectionException e) { //找不到readMethod,跳過 //throw new RuntimeException("No " + field.getName() + "'s ReadMethod founded in class["+clazz.getName()+"]"); continue; } if(i != (fieldArr.length -1)){ sb.append(","); } sb.append("\n"); } return primaryKey; } /** * 生成主鍵sql * @param tableName * @param sb * @param primaryKey */ private void generatePrimaryKey(StringBuilder sb, String tableName, String primaryKey) { if(StringUtils.isNotBlank(primaryKey)){ sb.append("alter table " + tableName + "\n"); sb.append(" add primary key (" + primaryKey + ")\n"); sb.append(" using index;"); } } /** * 得到Field最大長度 * @param clazz * @param fieldArr */ private int getMaxLength(Class clazz, Field[] fieldArr) { Field field; int maxLength = 0; for(int i=0;i<fieldArr.length;i++){ field = fieldArr[i]; PropertyDescriptor pd; try { pd = new PropertyDescriptor(field.getName(), clazz); Method mt = pd.getReadMethod(); if(null != mt){ if(!mt.isAnnotationPresent(Column.class)){ throw new RuntimeException("No Annotation[Column] founded in Method["+mt.getName()+"]"); } Column col = (Column)mt.getAnnotation(Column.class); int temLength = col.name().length(); if(temLength > maxLength){ maxLength = temLength; } } } catch (IntrospectionException e) { //找不到readMethod,跳過 //throw new RuntimeException("No " + field.getName() + "'s ReadMethod founded in class["+clazz.getName()+"]"); continue; } } return maxLength; } /** * * 將java類型轉換爲orable類型 * @param field * @param col * @return */ private String convertJava2Oracle(Field field, Column col){ //TODO 此處可根據本身須要作類型轉換 String type = "VARCHAR2"; if(field.getType().equals(String.class)){ type = "VARCHAR2(" + col.length() + ")"; }else if(field.getType().equals(java.util.Date.class) || field.getType().equals(java.sql.Date.class)){ type = "DATE"; }else if(field.getType().equals(Integer.class)){ type = "NUMBER"; } return type; } /** * * 輸出空格 * @param sb * @param n */ private void writeBlankSpace(StringBuilder sb, int n){ for(int i=0;i<n;i++){ sb.append(" "); } } }
1.TBankCode類,使用了註解@Table、@Id、@Column標識了表名、主鍵、列信息, DBGenerator能讀取類的註解信息,生成對應sql。(ps:因爲本人習慣將 Column加到get方法上,在開發過程當中限定DBGenerator 只讀取get方法上的註解,此外對應數據庫(oracle)的類型只作了幾個經常使用的轉換,可根據本身須要調整。)apache
package com.cg.db; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Table(name = "T_BANK_CODE") public class TBankCode implements java.io.Serializable{ /** * */ private static final long serialVersionUID = -9047055962822128897L; /** * 主鍵id */ private String id; /***省名 */ private String province; /*** 省代碼*/ private String provinceNo; /*** 地區名 */ private String area; /*** 地區代碼*/ private String areaNo; /*** 銀行行名稱*/ private String bankName; /*** 銀行號*/ private String bankNo; /*** 分行名稱 */ private String branchBankName; /*** 分行聯行號*/ private String branchBankCode; @Id @Column(name = "ID", unique = true, nullable = false, length = 40) public String getId() { return id; } public void setId(String id) { this.id = id; } @Column(name = "PROVINCE", length = 100) public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } @Column(name = "AREA", length = 100) public String getArea() { return area; } public void setArea(String area) { this.area = area; } @Column(name = "AREA_NO", length = 10) public String getAreaNo() { return areaNo; } public void setAreaNo(String areaNo) { this.areaNo = areaNo; } @Column(name = "PROVINCE_NO", length = 10) public String getProvinceNo() { return provinceNo; } public void setProvinceNo(String provinceNo) { this.provinceNo = provinceNo; } @Column(name = "BANK_NAME", length = 100) public String getBankName() { return bankName; } public void setBankName(String bankName) { this.bankName = bankName; } @Column(name = "BANK_NO", length = 30) public String getBankNo() { return bankNo; } public void setBankNo(String bankNo) { this.bankNo = bankNo; } @Column(name = "BRANCH_BANK_NAME", length = 100) public String getBranchBankName() { return branchBankName; } public void setBranchBankName(String branchBankName) { this.branchBankName = branchBankName; } @Column(name = "BRANCH_BANK_CODE", length = 30) public String getBranchBankCode() { return branchBankCode; } public void setBranchBankCode(String branchBankCode) { this.branchBankCode = branchBankCode; } }
2.實例化 DBGenerator 調用generate方法,傳遞參數TBankCode.classoracle
package com.cg.db; public class TestRun { public static void main(String[] args) { String a = new DBGenerator().generate(TBankCode.class); System.out.println(a); } }
3.運行結果app
-- Create table create table T_BANK_CODE ( ID VARCHAR2(40) not null, PROVINCE VARCHAR2(100), PROVINCE_NO VARCHAR2(10), AREA VARCHAR2(100), AREA_NO VARCHAR2(10), BANK_NAME VARCHAR2(100), BANK_NO VARCHAR2(30), BRANCH_BANK_NAME VARCHAR2(100), BRANCH_BANK_CODE VARCHAR2(30) ); alter table T_BANK_CODE add primary key (ID) using index;