搞过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的核心——反射 + 注解——就是这么玩的。

Java注解(2):实现自己的ORM的更多相关文章

  1. 基于java注解实现自己的orm框架

    ORM即Object Relation Mapping,Object就是对象,Relation就是关系数据库,Mapping映射,就是说Java中的对象和关系数据库中的表存在一种对应关系. 现在常见的 ...

  2. Java Spring Boot VS .NetCore (八) Java 注解 vs .NetCore Attribute

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  3. 使用Java注解自动化处理对应关系实现注释代码化

    概述 假设我们要从一个 ES 索引(相当于一张DB表)查询数据,ES表有 biz_no, type, status 等字段, 而应用对象则有属性 bizNo, type, status 等.这样,就会 ...

  4. Java 注解指导手册 – 终极向导

    原文链接 原文作者:Dani Buiza 译者:Toien Liu  校对:深海 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我们已经在Java Code Gee ...

  5. 【Java】java注解@Transient的作用, 配合JPA中时间段的查询

    java注解@Transient的作用 @Transient标注的属性,不会被ORM框架映射到数据库中. 用于数据库表字段和java实体属性不一致的时候,标注在属性上使用. 例如时间段的查询 查询 R ...

  6. Java 注解指导手册(下)

    9. 自定义注解   正如我们之前多次提及的,可以定义和实现自定义注解.本章我们即将探讨. 首先,定义一个注解:   public @interface CustomAnnotationClass   ...

  7. Java注解

    Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...

  8. 19.Java 注解

    19.Java注解 1.Java内置注解----注解代码 @Deprecated                                    //不推荐使用的过时方法 @Deprecated ...

  9. Java注解入门

    注解的分类   按运行机制分:   源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...

  10. java注解(Annotation)解析

    注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...

随机推荐

  1. 见微知著,细节上雕花:SVG生成矢量格式网站图标(Favicon)探究

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_215 Favicon是favorites icon的缩写,也被称为website icon(站点图标).page icon(页面图 ...

  2. pat链表专题训练+搜索专题

    本期题目包括: 1074:https://pintia.cn/problem-sets/994805342720868352/problems/994805394512134144 1052:http ...

  3. Excel 文本函数(一):LEFT、RIGHT 和 MID

    文本函数 LEFT.RIGHT 以及 MID 是非常常用的,它们用于截取文本字符串. LEFT(text, [num_chars]) 是从文本字符串的左边开始截取:RIGHT(text, [num_c ...

  4. Java jdk常用工具集合

    jdk 常用工具包目录: windows: 默认安装目录:C:\Program Files\Java\jdk1.8.0_152\bin> 1.查看Java进程 jps -l 查看当前机器的Jav ...

  5. 详解MySQL隔离级别

    一个事务具有ACID特性,也就是(Atomicity.Consistency.Isolation.Durability,即原子性.一致性.隔离性.持久性),本文主要讲解一下其中的Isolation,也 ...

  6. 字符串KMP——用途广泛的字符串匹配算法 + 扩展KMP——特殊定义的字符串匹配

    引 入 引入 引入 " SY 和 WYX 在看毛片.(几 毛 钱买到的动作 片,毛 片) WYX 突然想回味一个片段,但是只记得台词里面有一句挺长的 " ∗ ∗ ∗ ∗ **** ...

  7. MixCSE:困难样本在句子表示中的使用

    Unsupervised Sentence Representation via Contrastive Learning with Mixing Negatives 论文地址:https://www ...

  8. Knative部署应用以及应用的更新、应用的分流(二)

    1. 应用的更新 1.1 更新hello-example应用 1.更新应用的环境变量 可通过命令行的方式亦可以通过读取配置文件的方式,这里主要来看命令行的方式 [root@kn-server-mast ...

  9. 微服务系列之网关(二) konga配置操作

    1.konga核心对象 Kong 的四大核心对象:upstream,target,service,route.下面分别说: (1)upstream,字面意思上游,实际项目理解是对某一个服务的一个或者多 ...

  10. Hybrid app本地开发如何调用JSBridge

    前天同事问我公司内部的小程序怎么对接的,我回忆了一下,简单记录了一下前端同学需要注意的点. 背后还有小程序架构.网络策略等等.当时恰逢小程序架构调整,(老架构的时候我就发现了有一个问题点可以优化,但是 ...