搞过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. SpringBoot定时任务 - 集成quartz实现定时任务(单实例和分布式两种方式)

    最为常用定时任务框架是Quartz,并且Spring也集成了Quartz的框架,Quartz不仅支持单实例方式还支持分布式方式.本文主要介绍Quartz,基础的Quartz的集成案例本,以及实现基于数 ...

  2. CVI中调用数据库的几点..

    1.各类开发工具开发的程序调用数据库,有两种方式:ADO和ODBC. 2.以ODBC方式访问SQL数据库,必须在电脑管理工具----ODBC管理器 中配置TCP/IP项(IP1.IP2.IP3.IPA ...

  3. 运维实践-最新Nginx二进制构建编译lua-nginx-module动态链接Lua脚本访问Redis数据库读取静态资源隐式展现

    关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 希望各位看友[关注.点赞.评论.收藏.投币],助力每一个梦想. 本章目录 目录 0x0n 前言 ...

  4. 解决 idea无法下载源码 Sources not found for:XXX

    解决 idea无法下载源码 Sources not found for:XXX 命令行输入 mvn dependency:resolve -Dclassifier=sources 参考 https:/ ...

  5. codeforces600E Lomsat gelral【线段树合并/DSU】

    第一次AC这道题,是三年前的一个下午,也许晚上也说不定.当时使用的\(DSU\) \(on\) \(tree\)算法,如今已经淡忘,再学习新的算法过程中,却与旧物重逢.生活中充满不可知会的相遇,即使重 ...

  6. Docker 11 自定义镜像

    参考源 https://www.bilibili.com/video/BV1og4y1q7M4?spm_id_from=333.999.0.0 https://www.bilibili.com/vid ...

  7. java学习第二天多态.day09

    接口 接口总结 接口表示一种规约(规范.标准),它里面定义了一些列抽象方法(功能),它可以被多个类实现. 1接口名称首写字母用I,表示一个接口,后命名使用驼峰命名 2.接口中定义的都是抽象方法,所以可 ...

  8. MySQL字段类型与操作

    MYSQL字段类型与操作 字符编码与配置文件 操作 代码 功能 查看 \s 查看数据库基本信息(用户.字符编码) 配置(配置文件层面) my-default.ini windows下MySQL默认的配 ...

  9. 微软Azure配置中心 App Configuration (二):Feature Flag 功能开关特性

    写在前面 Web服务开发过程中我们经常有这样的需求: 某些功能我必须我修改了配置才启用,比如新用户注册送券等: 某个功能需到特定的时间才启用,过后就失效,比如春节活动等: 某些功能,我想先对10%的用 ...

  10. 什么?WPF 不支持 SVG ?

    什么?WPF 不支持 SVG ? 控件名:SharpVectors 作者:Elinam LLC (Japan) 项目地址: https://github.com/ElinamLLC/SharpVect ...