1. 简单对象关系库的功能
我们要实现的对象关系库的主要作用就是负责把数据库里面数据转化成Java对象,或者把Java对象转化成数据库里面的数据
现在存在数据库表PERSION
数据库Column | 描述 |
---|---|
id | 唯一主键id |
name | 名称 |
height | 身高 |
也存在Person类
public class Person{
private int id;
private String name;
private float height;
public Person(){}
public Person(String name,float height){
this.name=name;
this.height=height;
}
//省略setter与getter
}
我们需要能把类Person的实例直接插入数据库PERSON表中,也能直接把PERSON表中的数据查询出Person实例的集合
Database database=new Database();
Person zhangsan=new Person("张三",1.70);
//直接把对象插入数据库中
database.insert(zhangsan);
Person list=new Person("李四",1.50);
//插入对象集合
database.insert(list);
//直接从数据库中读取对象
List<Person> list=database.queryList(Persion.class);//直接查询出张三和李四的list
所以对象关系库能根据对象实例自动生成SQL语句,并且能根据对象类的信息自动把数据库的数据填入对象的属性中,这些功能就要靠反射和注解来实现。
我们要实现的是把一个
Person person=new Person("张三",1.70)
的对象变成一句INSERT INTO PERSON (NAME,HEIGHT) VALUES ('张三',1.70);
SQL语句
2. 反射
Java里面所有类(如前文的Person.class
)都是Class
类的一个实例
注意:Person.class是Class的一个实例,而不是Class的子类,这个要分清楚
System.out.println(Person.class.getClass())
输出的是class java.lang.Class
当我们用有一个类的Class时候,我们可以干很多事情,比如:
Person.class.newInstance();//通过反射创建一个Person的实例,但是保证Person具有零个参数的构造器,否则会抛出异常
Person.class.getConstructor();//获取Person的public构造器,这样子我们可以传入参数来构造Person实例了
Person.class.getFields();//获取Person的所有public属性
Person.class.getMethods();//获取Person所有public的方法
大家会发现API里面还有其他类似的函数
Person.class.getDeclaredConstructor();
Person.class.getDeclaredFields();
Person.class.getDeclaredMethods();
带
Declared
的方法与不带Declared
的方法的区别就是
1. 不带Declared
的方法获取的是声明为public的属性或者方法,带Declared
的方法获取的所有的属性或者方法(public,protected,private等)
2. 不带Declared
的方法获取的这个类所有的属性和方法,包括父类以及父类的父类等等,而带Declared
的方法获取仅仅是子类的属性和方法,不包括父类的属性和方法
既然现在我们能获取对象的属性了,那能否获取或者设置对象的属性值呢
Person zhangsan=new Person("张三",1.70);
Field name=Person.class.getDeclaredField("name");
name.setAccessible(true);//因为name是private属性的,所以需要设置成Accessible才能访问,否则会抛出异常
String zhangsanName=name.get(zhangsan);//获取张三的名字的属性值
name.set(zhangsan,"李四");//把张三的名字改成李四
现在我们既能设置对象属性值,又能获取对象属性值了,我们还需要做的就是确定对象的那些属性是跟数据库表column对应的,哪些是不需要对应的。这就需要我们对属性进行一些特别的标记
name.setAccessible(true)
的功能不是把name属性设置成public了
setAccessible
函数的功能是设置是否打开安全检查,setAccessible(true)
的作用就是关闭了属性的安全检查,这样不仅能访问private的属性,也大大的提高了反射的性能(毕竟减少了安全检查这个流程)
3. 注解
我们只需要知道Java的注解实际上就是对Java代码属性(类、方法、属性)一种额外标记(或者叫做说明)
public class Person{
@Column
private int id;
@Column
private String name;
@Column
private float height;
private String otherStuff;
}
通过上面的注解我们能够在程序中得到Person类中仅仅id,name,height
是作为数据库属性的。
注解除了标记Java代码还具有更多的功能
@Retention(RetentionPolicy.RUNTIME)//说明这个注解能够保持到运行期间
@Target(ElementType.TYPE)//这个注解用来描述类、接口、enum
@Inherited//这个注解能够被继承
@inteface Table{
String value() default "";//类对应的数据库表名
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//这个注解用来标记属性
@interface Column{
String name() default "";//属性对应的数据库Column的名称
int length() default 0;//属性的对应的数据库列的长度
boolean isPrimaryKey() default false;//是否为主键
}
@Table("PERSON")
public class Person{
@Column(length=20,isPrimaryKey=true)
private int id;
...//省略
}
从上面我们可以看到注解除了能标记以外还能包含更多的信息。
那么我们怎么获取注解呢?
//获取Person类的Table注解,如果没有则返回null
Table table = Person.class.getAnnotation(Table.class);
System.out.println(table.value());
//获取Person的id属性
Field idField=Person.class.getDeclaredField("id");
if(idField.isAnnotationPresent(Column.class)){//判断id属性是否存在Column注解
Column idColumn=idField.getAnnotation(Column.class);//获取id的Column注解
System.out.println(idColumn.length());
}
4. 性能问题
我们要记住反射和注解的使用虽然方便,但是也是需要消耗大量性能的。为了减少性能的消耗,我们可以把中间过程中产生的一些数据缓存下来,避免每次使用的都要重新生成
我们的目的是把一个Person对象通过反射和注解转化成一条SQL语句,这个过程可以分成两个步骤
- 通过反射和注解获取Person对象的表名和所有与数据库表对应的属性值
- 根据表名和属性拼装成一条SQL语句和属性值的集合
我们可以根据以上步骤进行缓存处理。
- 在第一步的时候把反射获取的表名与属性缓存下来
- 在第二步的时候把拼装的SQL语句缓存下来