重复造轮子(4) — 手写orm框架Ibernate

最近周边有许多朋友都来问我写框架不知道该如何开始下手,没有头绪,哈哈哈其实我一开始也是这样子慢慢熬过来的。所以今晚的这篇文章主要是讲解一些常规的orm框架的底层原理是如何实现的思路,同时也开源一套自己编写的orm框架。

常规的hibernate框架,mybatis框架而言,他们都有着自己的一些特点,就是将jdbc封装地特别方便,供开发者来引用。如果读者们有耐心,可以去看看hibernate的源码或者mybatis的源码,当然,仅仅只是学会看源码还是不够大。倘若能够自己实践一遍,就更加能够深入有所体会了。

常规的hibernate可以自动化生成相应的sql代码,但是对于性能优化方面需要开发人员学习hql语法,特别繁琐。而且Hibernate对于session生命周期管理也太过于复杂,配置也过于繁琐。现今比较流行的mybatis需要开发人员自己手动写sql来进行数据库操作,这种步骤较为灵活,但是对于一些简单的增删改查操作不能自动生成,例如一个插入语句,如果相应的表有1000个字段,那么这段sql语句将会特别冗长,不利于后期的维护。Ibernate结合了Hibernate和Mybatis两者的好处,简单的sql语句可以自动生成,复杂的sql语句可以自己编写。
本框架开发时长为1星期,由于目前已经大三了,7月份的时候需要找实习工作,所以只好最近抽时间来进行相应的优化。最近几天进行了版本的更新,如果各位感兴趣,可以到我的gitee上边去查看更加详细的内容:
https://gitee.com/IdeaHome_admin/ibernate

关于框架源码部分主要分为以下几点:

数据库连接池,自动创建接口实例,如何通过反射和注解构建数据库和实体类的映射,代理和asm字节码技术,缓存机制……
这些东西可能你在使用常规的spring,springmvc,mybatis,springdata,hibernate里面全部都有遇到过,也都有用到过它们的接口Api内容。但是这一次,我们通过自己手写来实现它。
是的,自己手写来实现这些,只要懂得javase即可。
使用的流程如下所示:
首先我们需要编写相应的properties配置文件:
这里写图片描述
给某个类标识好注解之后,做好映射工作。
这里写图片描述

然后便是设计dao的部分了:
这里写图片描述

最后通过junit进行测试:
这里写图片描述
这里写图片描述

关于本orm框架的设计:
1.数据库连接池

这个类主要是在org.lh.ibernate.config包里面,包里设置了以下几个类:
这里写图片描述
DBConfig类会自动去读取properties文件里面的内容:
这里写图片描述
是的,这个类的设计主要是用于存储一些静态常量,并没有其他的功能。
关于数据链接是采用了java.sql.Connection 这个类。
但是每个如果只是这样引用的话是不够的,还需要添加一个标识符用于表示这个链接是否是处于繁忙状态中。所以我单独构造了一个链接对象出来:
这里写图片描述
而相应的连接池对象主要就是用来创建一些列的链接,然后供外界去调用:
这个类实现了IDbPool接口,这样设计的好处,在于灵活,假若后期需要链接Oracle的话,在接口那边修改一下就好了。
这里写图片描述

这里写图片描述
这里写图片描述

最后就是一个连接池工厂了:
这里写图片描述
工厂的好处就在于,开发者不需要知道内部的具体细节,直接调用即可,非常方便。

2.自定义注解和常规配置类
1.3版本用到的注解还是比较少的,主要为以下几个:
这里写图片描述
关于注解的反射引用比较简单,这里面就直接跳过。
关于公用配置类在org.lh.ibernate.common里面,点开看看就懂了,这个比较简单。
这里写图片描述

3. Core核心包内容
通过一个对象,配置好相应的注解之后,如何去通过扫描识别属性,然后再自动生成sql返回对象,这个才是整个框架的核心之处。Ibernate里面涉及了一个叫做ISessionUtil的接口。它的具体实现类是org.lh.ibernate.core.impl.SessionUtil类。
首先构建SessionUtil对象的构造函数
这里写图片描述
至于这里面的构造参数为什么要有一个class对象,这个后边再说。
sessionUtil里面有很多有简单的函数:保存,更新,删除
这里写图片描述
例如说插入的实现方式主要在org.lh.ibernate.core.impl.InsertUtil
类里面进行。
主要的思路就是,通过反射获取数据表名的字段名字,然后通过相应传入对象的字段属性来进行赋值,动态创建sql语句进行插入即可。
这里写图片描述
为了防止sql注入,所以这里面的思路是找出需要插入的字段数量,创建足够多的占位符?,并且每个占位符映射的字段和实体类的字段顺序一致。
这个插入的实现还是比较简单的,稍微说一下里面的两个自定义函数吧:
getAttrFromFieldHasColumnAnnotation,它的功能是获取所有含有@column注解的字段的属性值,原理主要是采用反射的方式:具体实现在org.lh.ibernate.core.impl.FieldHandle里面
这里写图片描述
然后就是DbUtil里面的一个executeUpdate自定义函数,通过传入相应的sql语句,然后再去给sql里面的占位符赋值。但是有个前提,就是每个占位符对应的值要一一匹配好。

关于插入的功能基本也就这样了,如果读者能耐心地把这些看透,并且实现的话,可能还会发现很多有趣的部分。

同理,删除对象和更新对象的原理也基本是这样。这里就不一一介绍了。

查询操作:

查询操作比较另类,因为它需要和结果集进行动态映射,个人觉得比读操作要困难些。例如说查询操作:
这里写图片描述
这段代码的含义就是,当某个类如果标注了相应的缓存注解,就会先从缓存redis里面去查找,反之就是会从mysql里面查找。
关于缓存查找这个主要是用了jedis的api来进行搜索,具体代码在org.lh.ibernate.cache里面有,如果有部分api看不懂的话,百度上边找找大概就能搜索到了。

我们继续看核心部分:
这里写图片描述
如何能够生成sql语句,这个有了前边的插入,删除,更新经验之后,应该很快就能想到。但是如何才能动态化将ResultSet转换为相应的具体对象,这是一个难点。
慢慢来,经过思考之后,我设计了一个QueryUtil类,里面专门存放对于查询代码的封装方法,findOneObjBySql就是其中之一。
这里写图片描述
是的,你会发现这里面的前期部分就是动态生成sql语句,这个比较简单,后边用到了一个Dbutil的query方法,这个类在整个框架里面是进常会被用到的,而且非常核心。
这里写图片描述
这段源码只需要稍微了解一些jdbc基本都能读懂。
好了,返回了相应的ResultSet之后,就可以动态化的设置转换了。
这里面的ResultSetHandler实现原理可能有些复杂,需要比较耐心地去看下去才可以:
这里写图片描述
这里面出现了一个我在google上查到的jdk函数,叫做属性修改器PropertyDescriptor,是的,这已经是比较偏向于底层的东西了,如果你有耐心去研究下去的话,你的java能力一定会大涨的。
使用属性修改器,动态修改某个属性的值,然后再通过Method里面的invoke代理进行相应类的属性更新,这样就能创建一个新的对象了。
关于多个对象的结果集转换为List类型的函数,大概思路和上边的类似,这里就不多讲了
这里写图片描述
关于用户的自定义sql查询的底层原理又是如何的呢?
自定义sql查询方法的具体使用方式如下所示:
这里写图片描述
先创建一个ParameterLink对象,然后需要用这个对象add相应的占位符参数,需要一一匹配好,然后才能传入参数。
原理其实很简单,就是Dbutil里面的query方法。
关于Dbutil里面的query方法而言,里面包含有一个ParameterLink对象,这个对象的位置在org.lh.ibernate.core.tools里面,这个类主要是采用了一种链式编程的思想,有点像设计模式里面的责任链模式,所以在添加参数的时候会处理的比较灵活。这里需要注意一下,ParameterLink里面添加的内容需要和条件字段一一匹配。
关于ParameterLink的截图:
这里写图片描述
4.自定义接口创建Dao的具体实现

在Ibernate的1.3版本当中,最主要是添加了invoker这个包
这里写图片描述
这里面的到设计有点借鉴了mybatis和springdata的思想,通过开发者定义接口,然后在实际生产环境中动态化的生成实例类,从而完成相应的crud操作。
这里写图片描述
在IbernateBaseDao里面,有多个已经预定义好的crud函数,由于最近事情有点多,还有些许功能未有添加,希望各位开发者谅解。
这里写图片描述
开发中遇到的困难:
如何动态化地在jvm中通过接口来创建类实例;
使用asm字节码框架来获取某个函数参数名称;
如何更加高效和有用地封装繁琐的函数和类….

如果你对于我的源码表示看不懂,没关系,只要在看完之后,对框架有了更深的认识,小编我也就知足了。
可能各位读者会疑惑,为什么

如果对于源码有兴趣的朋友,可以到我的gitee上边去下载:
https://gitee.com/IdeaHome_admin/ibernate

感兴趣的读者也可以关注小编的个人公众号,我会定期在上边分享一些有价值的java知识内容

这里写图片描述

猜你喜欢

转载自blog.csdn.net/Danny_idea/article/details/81429390
今日推荐