手把手教你写一个java的orm(三)
使用反射解析class
上一篇我们完成了class到表映射关系的建立,但是这个并不能被代码正确处理,我们还需要让程序能够正确的识别这些映射关系。
这一篇主要讲的是建立一个从class到表的模型,使我们在class上添加的注解能够正确的被识别并处理。这里主要用到的是java中的反射相关的知识。不了解的同学请自行百度一下,不是很难~,另外这一篇也会稍微的提到一点反射的用法。
现在开始。
我们主要的需求是根绝我们添加的注解,生成各种类型的sql语句,所以我们首先要能够获取添加在java类名,属性,方法上的注解,并获取注解中的值。所以第一步:
获取特定的注解
获取class上的注解
在Java的Class中提供了.getAnnotation(annotationClass)的方法,
这里我在这个方法的基础上包了一层,主要是使用断言做了一些验证,在验证不通过的时候抛出我认识的异常信息。下面的方法都是如此处理的。
/**
* 获取类上的注解
*
* @param clz
* @param annotationClass
* @param <T>
* @return
*/
public static <T extends Annotation> T getAnnotation(Class<?> clz, Class<T> annotationClass) {
Assert.notNull(clz, CLASS_NOT_NULL);
Assert.notNull(annotationClass, ANNOTATIONCLASS_NOT_NULL);
return clz.getAnnotation(annotationClass);
}
获取属性上的注解
/**
* 获取属性上的注解
*
* @param field
* @param annotationClass
* @param <T>
* @return
*/
public static <T extends Annotation> T getAnnotation(Field field, Class<T> annotationClass) {
Assert.notNull(field, FIELD_NOT_NULL);
Assert.notNull(annotationClass, ANNOTATIONCLASS_NOT_NULL);
return field.getAnnotation(annotationClass);
}
这里我们获取注解的方法就写完了,可以通过这些方法获取我们需要的注解,并通过获取到的注解拿到其中的值。
大致是这样的:(这里的User.class)可以看 上一篇。
//代码(获取class上的注解)
@Test
public void getClassAnnotation() {
Table annotation = EntityUtils.getAnnotation(User.class, Table.class);
System.out.println(annotation.name());
}
//输出结果
user
//代码(获取field上的注解)
@Test
public void getFieldAnnotation() throws NoSuchFieldException {
Class userClass = User.class;
//getDeclaredField和getField是有一定区别的,这里用getDeclaredField
Field field = userClass.getDeclaredField("createDate");
Column annotation = EntityUtils.getAnnotation(field, Column.class);
System.out.println(annotation.name());
}
//输出结果
create_date
这样就可以获取到我们在class上添加的注解,以及注解中的值了。下面是第二步
获取Id以及Column
这里依然是通过反射实现,主要就是获取到这个class中的所有的属性(只属于这个class的,不包括父类)后,循环遍历一遍,根据每个属性上不同的注解加以区分就好了。这里为了简单,我定义了几个方法:
boolean isTable(Class aClass)
是不是添加了@Table
boolean isColumn(Field field)
是不是添加了@Column
boolean isId(Field field)
是不是添加了@Id
这几个方法的具体代码我就不贴出来了,很简单的。下面是取出一个class中所有属性的代码:
for (Field field : clz.getDeclaredFields()) {
if (isColumn(field)) {
//执行需要的操作。
}
}
在这个遍历个过程中我们可以新建一个类,里面用来存放表和class的各种对应关系。比如:
- class属性名称与表字段名称的对应。
- 表中的id是class中哪一个属性。
- 表的字段名称对应的是class里的那个个属性。
- 等等~~
代码大致是这样的 EntityTableRowMapper.java:
/**
* id的字段名称
*/
private String idName = null;
/**
* table对应的class
*/
private Class<T> tableClass = null;
/**
* 对应的数据库名称
*/
private String tableName = null;
/**
* 表中所有的字段
*/
private Set<String> columnNames = null;
/**
* 表中所有的字段对应的属性名称
*/
private Set<String> fieldNames = null;
/**
* 属性名称和数据库字段名的映射
* K: 属性名
* V:表字段名称
*/
private Map<String, String> fieldNameColumnMapper = null;
/**
* 数据库字段名和class属性的映射
* K:表字段名称
* V:class属性
*/
private Map<String, Field> columnFieldMapper = null;
这些用来描述表和class之间的关系就已经够用了。只要按照关系将里面的数据一一填充完毕就好。我写了一个方法来填充这些数据,代码是这样的:
//这里只是示例
Class clz = User.class();
//这里是主要代码
EntityTableRowMapper mapper = new EntityTableRowMapper();
Map<String, Field> columnFieldMap = EntityUtils.columnFieldMap(clz);
int size = columnFieldMap.size();
Map<String, String> fieldNameColumnMapper = new HashMap<>(size);
Set<String> columnNames = new HashSet<>(size);
Set<String> fieldNames = new HashSet<>(size);
mapper.setTableClass(clz);
mapper.setTableName(EntityUtils.tableName(clz));
mapper.setIdName(EntityUtils.idColumnName(clz));
mapper.setColumnFieldMapper(columnFieldMap);
for (Map.Entry<String, Field> entry : columnFieldMap.entrySet()) {
String columnName = entry.getKey();
Field field = entry.getValue();
String fieldName = field.getName();
fieldNameColumnMapper.put(fieldName, columnName);
fieldNames.add(fieldName);
columnNames.add(columnName);
}
mapper.setColumnNames(columnNames);
mapper.setFieldNameColumnMapper(fieldNameColumnMapper);
mapper.setFieldNames(fieldNames);
这里漏了一个Map<String, Field> columnFieldMap = EntityUtils.columnFieldMap(clz);的代码,在下面补上:
/**
* 获取Table的列名与Entity属性的映射Map
*
* @param clz
* @param <T>
* @return
*/
public static <T> Map<String, Field> columnFieldMap(Class<T> clz) {
Field[] declaredFields = clz.getDeclaredFields();
Map<String, Field> map = new HashMap<>(declaredFields.length);
for (Field field : declaredFields) {
if (isColumn(field)) {
map.put(columnName(field), field);
}
}
return map;
}
这时候,解析class里面的工作就完成了,下一步就是要通过拿到的数据来拼装sql了。
我下一篇再写_~
手把手教你写一个java的orm(三)的更多相关文章
- 手把手教你写一个java的orm(一)
写之前的说明 其实吧. 这个东西已经写好了,地址在:https://github.com/hjx601496320/JdbcPlus 这系列文章算是我写的过程的总结吧.(恩系列,说明我可能会写好久,╮ ...
- 手把手教你写一个java的orm(五)
生成sql:where 上一篇里我们实现了生成insert的sql,下面要开始实现update,delete,select的sql语句了.但是这些语句有一个比较麻烦的地方是:它们一般后面都会有wher ...
- 手把手教你写一个java的orm(二)
创建映射关系 想要实现一个orm的功能,我觉得就是要将class和数据库中的表创建映射关系.把class的名称和表的名称,class属性名称和表的字段名称,属性类型与表的字段类型一一对应起来.可以 ...
- 手把手教你写一个java的orm(完)
生成sql:select 上一篇讲了怎样生成一个sql中where的一部分,之后我们要做事情就简单很多了,就只要像最开始一样的生成各种sql语句就好了,之后只要再加上我们需要的条件,一个完整的sql就 ...
- 手把手教你写一个java的orm(四)
开始准备生成sql 在上一篇里,我们已经取到了我们在生成sql语句中所需要的信息,这一篇里我们开始根据class来生成我们需要的sql.在这之前我们先确认几件事情 sql里的参数我们使用占位符的形式. ...
- 只有20行Javascript代码!手把手教你写一个页面模板引擎
http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...
- 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...
- 手把手教你写一个RPC
1.1 RPC 是什么 定义:RPC(Remote Procedure Call Protocol)--远程过程调用协议 ,RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数 ...
- 让我手把手教你写一个强大、方便使用的 IOC 容器
一.介绍 1.介绍 最近无聊,也没什么事做,没事做总是要给自己找点事情做吧,毕竟人的生活在与折腾.于是,决定自己手动写一个 IOC 的框架.我们知道在 NetCore 的版本里面已经内置了 IOC 容 ...
随机推荐
- 程序媛计划——python爬虫
#用selenium打开百度首页 #第一次运行代码时应该在safari开发者选项中设置'allow remote automation' from selenium import webdriver ...
- leetcode 123. 买卖股票的最佳时机 III JAVA
题目: 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 两笔 交易. 注意: 你不能同时参与多笔交易(你必须在再次购买前出 ...
- Organising the Organisation(uva10766)(生成树计数)
Input Output Sample Input 5 5 2 3 1 3 4 4 5 1 4 5 3 4 1 1 1 4 3 0 2 Sample Output 3 8 3 题意: 有一张图上有\( ...
- 手把手教你从零搭建Python数据分析环境
由于最近再做推荐系统的特征处理,需要借助一些工具来筛选特征.最初使用了R,R的安装很简单,而且API也很容易使用,直接就能出图.后来,发现很多人在python和R之间做选择,所以我也在两个工具间摇摆不 ...
- webpack快速入门——CSS进阶:消除未使用的CSS
使用PurifyCSS可以大大减少CSS冗余 1.安装 cnpm i purifycss-webpack purify-css --save-dev 2.引入glob,因为我们需要同步检查html模板 ...
- Tcp下载文件
一.下载文件 tcp 客户端 1.创建套接字down_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)2.获取ip,portdown ...
- elasticsearch5.x安装中一些问题的解决办法
1.root超级用户不能正常启动 由于elasticsearch2.0版本以后不能使用root来启动,所以需要创建一个普通用户来启动. [root@bogon ~]# groupadd elastic ...
- CSS中的BFC详解
引言: 这篇文章是我对BFC的理解及总结,带你揭开BFC的面纱.你将会知道BFC是什么,形成BFC的条件,BFC的相关特性,以及他的实际应用. 一.何为BFC BFC(Block Formatting ...
- Binder AIDL中自定义类型传递的源码分析
binder机制实现的IPC和共享内存的方式不同,它采取的是值拷贝的方式,即进程间传递的实体遵循Parcelable协议, Bp端负责向Parcel里写东西,Bn端负责从Parcel里读取还原,顺序是 ...
- 腾讯云域名申请+ssl证书申请+springboot配置https
阿里云域名申请 域名申请比较简单,使用微信注册阿里云账号并登陆,点击产品,选择域名注册 输入你想注册的域名 进入域名购买页面,搜索可用的后缀及价格,越热门的后缀(.com,.cn)越贵一般,并且很可能 ...