搞过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. Java学习 (七)基础篇 变量

    变量 变量顾名思义,就是可以变化的量 Java是一种强类型语言,每个变量都必须声明其类型 Java变量是程序中最基本的存储单位,其要素包括变量名.变量类型和作用域 type varName [=val ...

  2. 飞书前端提到的竞态问题,在 Android 上怎么解决?

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...

  3. Luogu1064 金明的预算方案 (有依赖的背包)

    枚举多个状态 #include <iostream> #include <cstdio> #include <cstring> #include <algor ...

  4. 全局异常处理及参数校验-SpringBoot 2.7 实战基础 (建议收藏)

    优雅哥 SpringBoot 2.7 实战基础 - 08 - 全局异常处理及参数校验 前后端分离开发非常普遍,后端处理业务,为前端提供接口.服务中总会出现很多运行时异常和业务异常,本文主要讲解在 Sp ...

  5. 2022-08-15 - 初识MySQL

    MySQL数据库 数据库 数据库,又称为Database,简称DB.数据库就是一个文件集合. 顾名思义:是一个存储数据的仓库,实际上就是一堆文件,这些文件中存储了具有特定格式的数据,可以很方便的对里面 ...

  6. 实时降噪(Real-time Denoising):Spatio-Temporal Filtering

    目录 空间滤波(Spatial Filtering) 基于距离的高斯滤波 双边滤波(Bilateral filtering) 联合双边滤波(Joint Bilateral filtering)[201 ...

  7. 简单创建一个SpringCloud2021.0.3项目(一)

    目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 新建父模块和注册中心 1. 新建父模块 2. 新建注册中心Eureka 3. 新建配置中心Config 4. 新建两个业务服务 1. ...

  8. 【读书笔记】C#高级编程 第二十一章 任务、线程和同步

    (一)概述 所有需要等待的操作,例如,因为文件.数据库或网络访问都需要一定的时间,此时就可以启动一个新的线程,同时完成其他任务. 线程是程序中独立的指令流. (二)Paraller类 Paraller ...

  9. OKR之剑(理念篇)02—— OKR布道之旅

    作者:vivo互联网平台产品研发团队 1.我们是如何引入的 1.1.企业文化匹配 大概是在2013年底,一些创业者在硅谷深受OKR洗礼,并在自己的公司内小范围运用,以此OKR开始传入中国.而vivo初 ...

  10. Django 之必知必会三板斧

    一.HttpResponse 在django.http 模块中定义了HttpResponse 对象的API,HttpRequest 对象由Django 自动创建,不调用模板,直接返回数据. 1 在 a ...