您的位置:首页 > 其它

自已动手写ORM框架(2)——学前班

2015-07-14 14:27 197 查看
自已动手写ORM框架(2)——学前班

在前面已经进行了代码测试,数据库中的表记录和JAVA语言能进行很好的对应,然后我们怎么用面向对像的思想来开发一套通用的接口使得应用层从数据库中读写数据变得简单和简洁呢?
下面来测试ORM开发的终级思想基础,数据表的结构和字段数据类型与类的属性一一对应。
一、类和表的对应
假如我有一张Employee的数据表,包括5个字段。那采用类封装的方法测试代码如下:
//创建一个List容器,里面存储的是多条记录
List<Employee> list = new ArrayList<Employee>();

while(rs.next()){
//将数据库的一条记录封装到一个对像中
Employee emp = new Employee(rs.getInt(1),rs.getString(2),rs.getInt(3),
rs.getDouble(4),rs.getString(5));

//将对像添加到List容器中
list.add(emp);
}

for(Employee emp: list)
{
System.out.println(emp);
}
上面代码变的异常简洁,数据库的表和程序中的类,一一对应,那么程序对数据库进行读写将有归律可循,这也符合JavaBean的设计思想: 采用标准命名,属性的读写方法set/get都符合标准命名,那就可以通过反射获得属性的名子,从而拼接SQL语句来完成增,删,改,查的操作。

这样做的前提是了解JAVA的内省操作,很简单,目前只需要知道几个类和几个方法。

JavaBean基本测试代码如下:
//1. 获得Employee类的BeanInfo
BeanInfo employeeInfo = Introspector.getBeanInfo(Employee.class);

//2. 获得Employee类的属性
PropertyDescriptor[] fieldName = employeeInfo.getPropertyDescriptors();
for(int i=0; i<fieldName.length; i++){
System.out.println(fieldName[i].getName());
}

//3. 获得Employee类的方法
MethodDescriptor[] methodName = employeeInfo.getMethodDescriptors();
for(int i=0; i<methodName.length; i++){
System.out.println(methodName[i].getName());
}
上面代码的打印结果可以看出,对于属性,别忘了每一个类,都有一个class属性;而对于方法,不仅打印出了自身的方法,还打印出了父类的方法。
遍历方法数组,通过和反射结合,可以找到想要的特定的方法名,然后执行。

二、通过JavaBean获得对像的属性和get/set方法
在开始下面的程序测试之前,先看一下需求:在应用层,我并不懂数据库和JDBC,我是纯粹的面向对像编程,我有一个对像,我需要把它存储到数据库中,而我不需要知道数据库和JDBC的任何和识,我只像以下代码这样调用接口就可以了:
//创建一个对像
Persion p = new new Persion(001,"刘德华","香港",50);
insert(p);

//创建另一个对像
Animal cat = new Animal(001,"旺财",3);
insert(cat);
也就是说,我提供一套统一的接口,接口内部自会根据传过来的对像来解析到它的属性和对应的值,然后自动构建SQL语句插入到和对像对应的表中。这样就可以对代码进行重用。还记得前面说的学生成绩管理系统吗,我不管是插入班级,学生,还是课程,都可以调用统一的insert方法,而这个方法只写一次就可以了,这样减少了代码的冗余。

1. 取得属性和其对应方法
其实,通过反射也是可以取得属性和方法,但是这在每一个属生和它的set/get方法去对应的时候将变得困难。而JavaBean则提供了相关的类和方法使得我们只要知道一个属生的名子,就可以获得对应的方法。测试代码如下:
// 1.通过JavaBean获得对像所在类的属性
BeanInfo bean = null;
try
{
bean = Introspector.getBeanInfo(obj.getClass());
} catch (IntrospectionException e)
{
e.printStackTrace();
}

//2. 获得所有的属性,除了id和class
PropertyDescriptor[] propNames =  bean.getPropertyDescriptors();

List<String> fieldNames = new ArrayList<String>();	//类的属性列表
for(int i = 0; i < propNames.length; i ++){
if(!"id".equalsIgnoreCase(propNames[i].getName()) &&
!"class".equalsIgnoreCase(propNames[i].getName())){
fieldNames.add(propNames[i].getName());
}
}

//2.获得类的属性所对应的get/set方法
for(int i=0; i<fieldNames.size(); i++){
String fieldName = fieldNames.get(i);	//首先得到属性名
//查找属性名对应的PropertyDescriptor
PropertyDescriptor propDes = findPropertyDescriptor(fieldName,propNames);

//根据属性的PropertyDescriptor得到属性,set方法名,get方法名
System.out.println(propDes.getName()+"\t"+propDes.getReadMethod().getName()+"\t"+propDes.getWriteMethod().getName());

}
相比反射,采用JavaBean在属性和方法的一一对应上变得异常简单。

2. 得到对像的属性所对应的值
//2.获得类的属性所对应的get/set方法
for(int i=0; i<fieldNames.size(); i++){
String fieldName = fieldNames.get(i);	//首先得到属性名
//查找属性名对应的PropertyDescriptor
PropertyDescriptor propDes = findPropertyDescriptor(fieldName,propNames);

System.out.println(propDes.getName()+" "+	//打印属性名
propDes.getReadMethod().invoke(obj)); //执行它的get方法得到属性值
}
从上面可以看出,在getReadMethod后面可以直接执行invoke来得到属性的值,是不是好牛B?

三、ORM开发终级思想
首先,分析一下我们为什么需要ORM,ORM为什么能实现出来。
1. 对数据的插入,SQL语句的语法为: insert into 表名(字段名1,字段名2) values(值1,值2)。
2. 对数据的删除,SQL语句的语法为: delete from 表名 where id=xxx。
3. 对数据的修改,SQL语句的语法为: update 表示 set 字段名=值 where id=xxx。
对于上面的语句,我写的并不是很准确,但可以看出一些规律来,表和类对应,那表名就可以通过反射得到,字段名可通过反得到,value值可以通过字段对应的方法获得,而id又包含在类的属性中。
所以,以对像为单位进行数据的存储是可行的!而不管JDBC底层和数据库的操作。

总结:
在应用层传过来的是一个具体的对像obj的情况下:
1. 获得类名 ——> 表名
//获得类名,也即表名
Class clazz = obj.getClass();
String className = clazz.getSimpleName();
2. 通过JavaBean获得类的字段
//获得BeanInfo对像
BeanInfo bean = Introspector.getBeanInfo(obj.getClass());
//获得类的属性名
PropertyDescriptor[] propNames =  bean.getPropertyDescriptors();
3. 获得属性对应的名称,并找得到其对应的PropertyDescriptor
//遍历属性List
for(int i=0; i<fieldNames.size(); i++){
String fieldName = fieldNames.get(i);	//首先得到属性名
//根据属性名查找属性名对应的PropertyDescriptor
PropertyDescriptor propDes = findPropertyDescriptor(fieldName,propNames);
}

/**
* 从PropertyDescriptor数据中找到与属性名子propName对应的描述符
* @param propName
* @param proDesc
* @return
*/
private static PropertyDescriptor findPropertyDescriptor(String propName,PropertyDescriptor[] proDesc){
for(int i=0; i<proDesc.length; i++){
if(propName.equals(proDesc[i].getName())){
return proDesc[i];
}
}
return null;
}
得到了PropertyDescriptor 一切都好说了!

4. 构建SQL语句
有了上面的这些东西,我们就知道了表名,字段名,还可以通过 PropertyDescriptor获得字段对应的值,那么我们就可以构建出完成的SQL语句了,然后采用JDBC来执行该SQL语句就OK。

附 一些类和方法的说明:
1. BeanInfo类:它的功能和目的就是为了获取对bean属性的控制权。这样的话,只需要提供属性名和所属的bean类,就可以为每个属性构建一个PropertyDescriptor。

PropertyDescriptor des = new PropertyDescriptor("name", Persion.class)
通常采用上面的方法去构建一个PropertyDescriptor,然而我们最常用的是直接获得它。

通常采用Introspector类中的静态方法getBeanInfo来构建一个BeabInfo类。它的用方在上面代码中已经有体现。

BeanInfo类中的getPropertyDescriptors方法返回一个PropertyDescriptor数据,它其中包含了所有的属性描述符。

2. PropertyDescriptor类:在使用BeanInfo类的方法获得所有属性的描述符数组后,就可以操作一个个的属性了,它有getName方法获得属性的名子,而有了名子,就可以从数组中找出对应的描述符,上面代码中的有一个函数已经实现了这种方法,它的目的就是得到特定属性的PropertyDescriptor对像,从而利用它的方法getReadMethod和getWriteMethod方法来get/set属性的值。
这两个方法的原型如下:
Method getReadMethod()  获得应该用于读取属性值的方法。
Method getWriteMethod() 获得应该用于写入属性值的方法。
注意到返回的是Method类型,通过反射中执行方法的知识我们可以很容易的利用它们。如:
propDes.getReadMethod().invoke(obj)
这样就得到了传过来对像的属性的值,其中propDes是这个属性的描述符。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: