Java注解(2):实现自己的ORM

105 阅读2分钟

您好,我是湘王,这是我的掘金小站,欢迎您来,欢迎您再来~

搞过Java的码农都知道,在J2EE开发中一个(确切地说,应该是一类)很重要的框架,那就是ORM(Object Relational Mapping,对象关系映射)。它把Java中的类和数据库中的表关联起来,可以像操作对象那样操作数据表,十分方便。给码农们节约了大量的时间去摸鱼。其实它的本质一点都不复杂,而最核心的就是怎么实现对象和表之间的转换。之前对反射和注解有了一点了解,所以就试着来实现咱们自己的缝合怪。

首先,需要建立一个「表格」:

/**
 * 类注解,将类注解成数据库表
 *
 * @author xiangwang
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    String name() default "";
}

 

然后,定义需要的数据库数据类型:

/**
 * 字段类型枚举
 *
 * @author xiangwang
 */
public enum Type {
    CHAR,
    STRING,
    BOOLEAN,
    INTEGER,
    LONG,
    FLOAT,
    DOUBLE,
    DATETIME
}

/**
 * 数据库字段类型
 *
 * @author xiangwang
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnType {
    Type value() default Type.INTEGER;
}

 

再来完善字段相关信息:

`/***
**** 字段信息*


*** @author xiangwang**
*/
*@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtraInfo {
String name() default "";
int length() default 0;
}

 

/***
**** 明确字段约束*


*** @author xiangwang**
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
// 还可以增加默认值

*}`

 

把他们拼起来,成为完整的字段描述:

/**
 * 拼装注解,形成完整的字段嵌套注解
 *
 * @author xiangwang
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {
    ColumnType columntype() default @ColumnType;
    ExtraInfo extrainfo() default @ExtraInfo;
    Constraints constraints() default @Constraints;
}

 

最后,创建实体类,应用刚才写好的这些注解:

`/***
**** 用户实体类*


*** @author xiangwang**
*/
*@DBTable(name = "User")
public class User {
@TableColumn(
columntype = @ColumnType(Type.INTEGER),
extrainfo = @ExtraInfo(name = "id", length = 4),
constraints = @Constraints(primaryKey = true))
private String id;

   @TableColumn(
columntype = @ColumnType(Type.STRING),
extrainfo = @ExtraInfo(name = "name", length = 32),
constraints = @Constraints(primaryKey = false, allowNull = false, unique = true))
private String name;

   @TableColumn(
columntype = @ColumnType(Type.INTEGER),
extrainfo = @ExtraInfo(name = "age", length = 4),
constraints = @Constraints(primaryKey = false))
private Integer age;

   public String getId() { return id; }
public void setId(String id) { this.id = id; }

   public String getName() { return name; }
public void setName(String name) { this.name = name; }

   public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }

   @Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}`  

来看看ORM是怎么工作的吧:

/**
 * 解析类型注解
 */
private static String getColumnType(ColumnType columntype) {
    String type = "";
    switch (columntype.value()) {
        case CHAR:
            type += "CHAR";
            break;
        case STRING:
            type += "VARCHAR";
            break;
        case BOOLEAN:
            type += "BIT";
            break;
        case INTEGER:
            type += "INT";
            break;
        case LONG:
            type += "BIGINT";
            break;
        case FLOAT:
            type += "FLOAT";
            break;
        case DOUBLE:
            type += "DOUBLE";
            break;
        case DATETIME:
            type += "DATETIME";
            break;
        default:
            type += "VARCHAR";
            break;
    }
    return type;
}

/**
 * 解析信息注解
 */
private static String getExtraInfo(ExtraInfo extrainfo) {
    String info = "";
    if (null != extrainfo.name()) {
        info = extrainfo.name();
    } else {
        return null;
    }
    if (0 < extrainfo.length()) {
        info += " (" + extrainfo.length() + ")";
    } else {
        return null;
    }
    return info;
}

/**
 * 解析约束注解
 */
private static String getConstraints(Constraints con) {
    String constraints = "";
    if (con.primaryKey()) {
        constraints += " PRIMARY KEY";
    }
    if (!con.allowNull()) {
        constraints += " NOT NULL";
    }
    if (con.unique()) {
        constraints += " UNIQUE";
    }

    return constraints;
}

 

做了那么多的铺垫,终于到了临门一脚了,实现一个缝合怪了:

/**
 * 临门一脚:实现一个缝合怪
 */
private static void createTable(List<String> list) {
    for (String className : list) {
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
            DBTable dbTable = clazz.getAnnotation(DBTable.class);
            if (dbTable == null) {// 无DBTable注解
                continue;
            }
            // 转大写
            String tableName = clazz.getSimpleName().toUpperCase();
            StringBuilder sql = new StringBuilder("CREATE TABLE " + tableName + "(");
            for (Field field : clazz.getDeclaredFields()) {
                // 反射得到注解
                Annotation[] anns = field.getDeclaredAnnotations();
                if (anns.length < 1) {
                    continue;
                }
                String columnInfo = "";
                // 类型判断
                if (anns[0] instanceof TableColumn) {
                    TableColumn column = (TableColumn) anns[0];
                    String type = getColumnType(column.columntype());
                    columnInfo = getExtraInfo(column.extrainfo());
                    // 代替(
                    columnInfo = columnInfo.replace("(", type + "(");
                    columnInfo += getConstraints(column.constraints());
                }
                sql.append("\n " + columnInfo + ",");
            }
            // 删除尾部的逗号
            String tableCreate = sql.substring(0, sql.length() - 1) + "\n);";
            System.out.println(tableCreate);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

验证效果的时候到了:

public static void main(String[] args) {
    Class<?> clazz = User.class;
    List<String> list = new ArrayList<>();
    list.add(clazz.getName());

    createTable(list);
}

当然,实际的运营于生产环境中的ORM框架可要比这个小玩意复杂多了。但千变万变,原理不变,ORM的核心——反射 + 注解——就是这么玩的。

感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~