反射+枚举+freemarker,自动生成实体类,自动建表建索引(一)之生成实体类,枚举详解

来源:互联网 发布:虚拟机安装mac很卡 编辑:程序博客网 时间:2024/05/15 04:21
  用反射+枚举+freemarker,自己实现的自动生成实体类和自动建立数据表。用enum枚举作为数据表的配置文件,1个枚举就是1张表,根据枚举类,自动生成实体类,和自动建表。下面先介绍自动生成实体类。
  主要步骤就是先反射读取枚举类,获取所需信息,然后用freemarker生成实体类。这里需要用到freemarker.jar这个jar包(点击下载)。

  1、普及下enum枚举,既然用到了,就要好好深入了解一下。

  其实枚举是个特殊的java类。创建枚举类型要使用 enum 关键字,隐含了所创建的类型都是 java.lang.Enum 类的子类(java.lang.Enum 是一个抽象类)。枚举类型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚举类型的名称。枚举类型的每一个值都将映射到 protected Enum(String name, int ordinal) 构造函数中,在这里,每个值的名称都被转换成一个字符串,并且序数设置表示了此设置被创建的顺序(默认是0,1,2,3...)。
  原来定义的常量一般用接口来定义,如下:
public interface IConstants {    String MON = "Mon";    String TUE = "Tue";    String WED = "Wed";    String THU = "Thu";    String FRI = "Fri";    String SAT = "Sat";    String SUN = "Sun";}


  现在用enum定义如下:
package com.pzb.test;public enum EnumTest {    MON, TUE, WED, THU, FRI, SAT, SUN;}


  实际上是调用了如下方法数次:
new Enum<EnumTest>("MON",0);new Enum<EnumTest>("TUE",1);new Enum<EnumTest>("WED",2);


  然后是介绍下枚举常用的方法:
int compareTo(E o)           //比较此枚举与指定对象的顺序。Class<E> getDeclaringClass()           //返回与此枚举常量的枚举类型相对应的 Class 对象。String name()          //返回此枚举常量的名称,在其枚举声明中对其进行声明。int ordinal()           //返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。String toString()          // 返回枚举常量的名称,它包含在声明中。static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)           //返回带指定名称的指定枚举类型的枚举常量。


  然后是原理分析:enum 的语法结构尽管和 class 的语法不一样,但是经过编译器编译之后产生的是一个class文件。该class文件经过反编译可以看到实际上是生成了一个类,该类继承了java.lang.Enum<E>。EnumTest 经过反编译(javap com.hmw.test.EnumTest 命令)之后得到的内容如下:
public class com.hmw.test.EnumTest extends java.lang.Enum{    public static final com.hmw.test.EnumTest MON;    public static final com.hmw.test.EnumTest TUE;    public static final com.hmw.test.EnumTest WED;    public static final com.hmw.test.EnumTest THU;    public static final com.hmw.test.EnumTest FRI;    public static final com.hmw.test.EnumTest SAT;    public static final com.hmw.test.EnumTest SUN;    static {};    public int getValue();    public boolean isRest();    public static com.hmw.test.EnumTest[] values();    public static com.hmw.test.EnumTest valueOf(java.lang.String);    com.hmw.test.EnumTest(java.lang.String, int, int, com.hmw.test.EnumTest);}


  所以,实际上 enum 就是一个 class,只不过 java 编译器帮我们做了语法的解析和编译而已。可以把 enum 看成是一个普通的 class,它们都可以定义一些属性和方法,不同之处是:enum 不能使用 extends 关键字继承其他类,因为 enum 已经继承了 java.lang.Enum(java是单一继承)。
  
  

  2、开始用枚举定义数据表,其重要用到一个自己定义的类,用来作为数据结构。具体看代码,代码注释的也比较详细了。

package com.test.common;/** * 枚举用到的数据结构,用来存储表中字段属性(长度,索引,是否为空,默认值) * @author Lufeng * */public class EntityConfigData {//表中字段属性名称常量public static final String TYPE = "type";public static final String LENGTH = "length";public static final String INDEX = "index";public static final String NULLABLE = "nullable";public static final String DEFAULTS = "defaults";//不同类型字段的长度默认值public static final int TYPE_DEFUALT_INT = 11;public static final int TYPE_DEFUALT_LONG = 20;public static final int TYPE_DEFUALT_STRING = 50;//表中字段属性(长度,索引,是否为空,默认值)public Class<?> type;public int length;public boolean index;public boolean nullable;public String defaults;public EntityConfigData(Class<?> clazz) {this(clazz, new Object[0]);}public EntityConfigData(Class<?> clazz, Object...params) {this.type = clazz;//依次遍历参数param并赋值int len = params.length;for(int i = 0; i < len; i += 2){Object key = params[i];Object val = params[i + 1];//如果是长度if(LENGTH.equals(key)) {this.length = Integer.parseInt(val.toString());} else if (INDEX.equals(key)) {//如果是索引this.index = Boolean.parseBoolean(val.toString());;} else if (NULLABLE.equals(key)) {//如果是可否为空this.nullable =Boolean.parseBoolean(val.toString());} else if (DEFAULTS.equals(key)) {//如果是默认值this.defaults = val.toString();}}}}

package com.test.common;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 枚举用到的注解,用来表示将要生成的实体类名,和要建立的数据表名 * @author Lufeng * */@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface EntityConfig {String entityName();//实体类名String tableName();//数据表名}

package com.test.testentity;import static com.test.common.EntityConfigData.DEFAULTS;import static com.test.common.EntityConfigData.INDEX;import static com.test.common.EntityConfigData.LENGTH;import static com.test.common.EntityConfigData.NULLABLE;import com.pwrd.test.EntityConfig;import com.pwrd.test.EntityConfigData;/** * 这个就是主要的枚举类 * @author Lufeng * */@EntityConfig(entityName = "Character", tableName = "zh_character")public enum EntityCharacter {account(String.class), name(String.class, LENGTH, 12),profession(String.class, INDEX, true), sex(String.class), level(String.class), test(String.class, LENGTH, 6, INDEX, true, NULLABLE, true, DEFAULTS, "test"),testnew(int.class, LENGTH, 6, INDEX, true, DEFAULTS, 100),;public EntityConfigData conf;private EntityCharacter(Class<?> clazz, Object...params) {conf = new EntityConfigData(clazz, params);}}



  3、现在的任务就是反射获取枚举类,然后用freemarker生成想要的实体类。

package com.test.common;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.Writer;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Locale;import java.util.Map;import java.util.Set;import com.test.support.PackageClass;import freemarker.template.Configuration;import freemarker.template.Template;/** * 自动生成实体类的核心类 * @author Lufeng * */public class GenEntity {private String pageEncoding = "UTF-8";private Configuration configuration;//FreeMarker配置private String sourceDir;//配置源文件夹private String targetDir;//输出目标文件夹/** *  创建和调整配置,这个在生命周期中只做一次 */public GenEntity(String sourceDir, String targetDir) {this.sourceDir = sourceDir; this.targetDir = targetDir;configuration = new Configuration();try {String tempDir = System.getProperty("user.dir") + "/src/" + PackageClass.packageToPath(sourceDir) + "/templates";configuration.setDirectoryForTemplateLoading(new File(tempDir));configuration.setEncoding(Locale.getDefault(), pageEncoding);} catch (IOException e) {throw new RuntimeException(e);}}/** * 生成配置源文件夹下的所有实体类 */public void genFiles() {// 获取源文件夹下的所有类Set<Class<?>> sources = PackageClass.find(sourceDir);// 遍历所有类,取出有注解的生成实体类for(Class<?> clazz : sources) {// 过滤没有EntityConfig注解的类if(clazz.isAnnotationPresent(EntityConfig.class)) {genFile(clazz);}}}/** * 生成java实体类的核心方法 * @param clazz */public void genFile(Class<?> clazz) {try {genFileHandler(clazz);} catch (Exception e) {throw new RuntimeException(e);}}/** * 生成java实体类的核心方法 * @param clazz * @throws Exception  */private void genFileHandler(Class<?> clazz) throws Exception {/* 1,获取模板 */Template temp = configuration.getTemplate("testEntity.ftl", pageEncoding);temp.setEncoding(pageEncoding);/* 2,设置模板的数据内容Map */// 获取实体类名,表名,包名String entityName = GenUtils.getEntityName(clazz);String tableName = GenUtils.getTableName(clazz);String packageName = clazz.getPackage().getName();// 填充MapMap<String, Object> targetClazz = new HashMap<String, Object>();List<Map<String, String>> fields = new ArrayList<Map<String, String>>();targetClazz.put("packageName", packageName);targetClazz.put("entityName", entityName);targetClazz.put("tableName", tableName);targetClazz.put("fields", fields);// 获得所有枚举字段成员(id, account, name, profession...),// 遍历每个枚举成员,获取属性,放入Map中Object[] enums = clazz.getEnumConstants();for (Object e : enums) {Map<String, String> field = new HashMap<String, String>();String name = e.toString();Class<?> type = (Class<?>) GenUtils.getFieldValue(clazz, e, "type");field.put("name", name);field.put("type", type.getSimpleName());fields.add(field);}// 判断目标文件夹不存在,则新建文件夹String targetFileDir = this.targetDir + PackageClass.packageToPath(packageName) + "/";File dir = new File(targetFileDir);if(!dir.exists()) dir.mkdirs();/* 3,将模板和数据合并,并生成文件 */String fileName = targetFileDir + entityName + ".java";System.out.println("-------开始生成" + fileName + "文件......------");File target = new File(fileName);Writer out = new OutputStreamWriter(new FileOutputStream(target), pageEncoding);temp.process(targetClazz, out);out.flush();out.close();System.out.println("-------" + fileName + "文件生成完毕!-------\n");}public static void main(String[] args) {String dir = System.getProperty("user.dir") + "/../pwvcommon/";args = new String[] {"com.pwrd.game.testentity", dir};String sourceDir = args[0];String targetDir = args[1];GenEntity generator = new GenEntity(sourceDir, targetDir);generator.genFiles();}}


  生成完毕,截图如下:



  4、附件

  获取注解信息的工具类
package com.test.common;import java.lang.reflect.Field;/** * 工具类 * @author Lufeng * */public class GenUtils {/** * 从注解中获取类名 * @param clazz * @return  */public static String getEntityName(Class<?> clazz) {EntityConfig config = clazz.getAnnotation(EntityConfig.class);String entityName = config.entityName();return entityName;}/** * 从注解中获取表名 * @param clazz * @return */public static String getTableName(Class<?> clazz) {EntityConfig config = clazz.getAnnotation(EntityConfig.class);String tableName = config.tableName();return tableName;}/** * 获取class的object字段的name字段值 * @param clazz * @param object * @param name * @return * @throws Exception */public static Object getFieldValue(Class<?> clazz, Object object, String name) throws Exception {//遍历所有属性,获取EntityConfigBase属性Field[] fields = object.getClass().getFields();Field field = null;for(Field f : fields) {if(f.get(object) instanceof EntityConfigData) {field = f;break;}}//获取EntityConfigBase中name字段值if(field == null) return null;Object obj = field.get(object);Object result = obj.getClass().getField(name).get(obj);return result;}}

  模板testEntity.ftl内容:
package com.test.entity;import com.test.MysqlRecord;import com.test.common.DbField;public class ${entityName} extends EntityBase {@Overridepublic String getTableName() {return "${tableName?lower_case}";}public ${entityName}() {super();}public ${entityName}(MysqlRecord record) {super(record);}public long getId() {return record.get("id");}public void setId(long id) {return record.set("id", id);}<#list fields as field><#-- 根据不同的类型,设置不同的方法名 <#if field.type == "int"><#assign getMethod = "getI32"><#assign setMethod = "setI32"><#elseif field.type == "long"><#assign getMethod = "getI64"><#assign setMethod = "setI64"><#elseif field.type == "String"><#assign getMethod = "getStr"><#assign setMethod = "setString"></#if>-->public ${field.type} get${field.name?cap_first}() {return record.${getMethod}(${enumName}.${field.name}.ordinal());}public void set${field.name?cap_first}(${field.type} ${field.name}) {record.${setMethod}(${enumName}.${field.name}.ordinal(), ${field.name});}</#list>}


具体的自动建表,详见下一篇博文:反射+枚举+freemarker,自动生成实体类,自动建表建索引(二)之建表建索引,注解和DatabaseMetaData获取信息。
注:这个是本人原创的,如需转载,请尊重劳动果实,务必保持原链接(http://blog.csdn.net/lufeng20/article/details/8730604)。
原创粉丝点击