JAVA & 数据库编程 & ORM工具(上)
- 1、从普通数据库连接到ORM的遐想
- 2、配置文件
- 3、完成类与表的映射关系
1、从普通数据库连接到ORM的遐想
如何通过jdbc让java语言和数据“联通”起来,想必是不难的。
try {
//1
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/school?"
+ "useSSL=false", "root", "个人密码(例如123123)");
//2
String sqlStr = "SELECT id, name, age FROM student";
PreparedStatement statement = connection.prepareStatement(sqlStr);
ResultSet rSet = statement.executeQuery();
//3
while(rSet.next()) {
String stuID = rSet.getString("id");
String stuName = rSet.getString("name");
String stuAge = rSet.getString("age");
System.out.println(stuID +" "+ stuName +" " + stuAge);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
不难看出,以上程序有三大步骤。
- 链接数据库
- 构造并执行失去了语句
- 处理从数据库中得到的结果
对于每次对数据库的获取而言,步骤二和三是不可缺少的,但是对于链接数据库这一操作,是可以不用每使用一次就必须重新建立连接的。
当然,如果只是发现这一点,是不会想到ORM的。那么仔细研究数据表的模式和我们对数据库表的操作。
1、数据库是由:表、记录组成;
SELECT查找的是由多条记录组成的“表”;
INSERT INTO 插入的是一条相对完整的记录;
UPDATE 是对一条,或多条复合要求的记录进行修改;
DELETE 删除的是复合条件的记录……
2、Java的面向对象编程思想:
类;
对象。
将上述两种不同的操作,按照数据对等性,可以这样看:
表 <=> 类
表是由多个字段描述而成的;
类是由多个成员描述而成的;
记录 <=> 对象
记录是那些字段的某一次取值的集合;
对象是那些成员的某一次取值的集合。
在研究我们对于数据的增删改查
这些操作的统一SQL语句可以描述为:
查找指定的一条记录:
SELECT 字段表
FROM 表名称
WHERE 关键字段=value;
查找所有记录:
SELECT 字段表
FROM 表名称;
插入一条记录:
INSERT INTO 表名称
(字段表)
VALUES (字段值表);
……
上述SQL语句的创建,可不可以“自动化”?如果能自动化,那么,这些
SQL语句的执行更容易自动化……
上述想法能得以实现的关键不确定点是:
字段表;
字段名称;
字段的值;
从结果集中取得的记录存储方式……
如果上述问题能够得以解决,那么,有关数据库表访问,完全可以
自动化.
最终引出我们的ORM:Object Relation Mapping
Object就是Java的对象;
Relation就是关系型数据库;
Mapping就是“映射关系”。
同时,让我们看看最后完成工具的时候,使用数据库如何轻松。
其中实现的PreparedStatement,一定程度上防止了sql注入
2、配置文件
第一个配置文件,是从个人连接数据库的必要数据中提取出来的。
固定的驱动类名,固定的个人数据库位置(自定义),用户名(使用者自行配置),密码(使用者自行配置)。
property文件。
第二个配置文件,也是实现ORM的基础。就是表与类之间的关系映射。
举个例子:
上述是一张学生信息的表(虽然数据很少)。表名是 student ,位于school数据库下。表有三个属性,分别是id, name, age。其中id为主键。
那么用一个类来如何描述呢?
我们建立一个模板类。就叫做学生信息。
public class StudentInfo {
private String stuId;
private String stuName;
private String stuAge;
public StudentInfo() {
}
public String getStuId() {
return stuId;
}
public void setStuId(String stuId) {
this.stuId = stuId;
}
//省略些,get,set方法
@Override
public String toString() {
return "StudentInfo [stuId=" + stuId + ", stuName=" + stuName + ", stuAge=" + stuAge + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((stuId == null) ? 0 : stuId.hashCode());
return result;
}
//stuId作为两个实例是否不同的筛选因子。对于表中的主键。
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StudentInfo other = (StudentInfo) obj;
if (stuId == null) {
if (other.stuId != null)
return false;
} else if (!stuId.equals(other.stuId))
return false;
return true;
}
}
看完表,再看完类,是否找到了映射关系?xml文件配置自然来。
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<mapping class = "com.mec.about_orm.model.StudentInfo" table = "student">//类和表一对一
<field name = "stuId" cloumn = "id"></field>//cloumn 列,属性,对应着类的成员名
<field name = "stuName" cloumn = "name"></field>
<field name = "stuAge" cloumn = "age"></field>
<key name = "id"></key>
</mapping>
</mappings>
3、完成类与表的映射关系
此时我们在仔细看看我们拥有什么
- 一张xml文件
- 一个java存储数据的模板类
- 一个数据库中一张完整的表
那么关键,自然是在xml文件上。
我们需要做的工作就是,扫描xml文件形成,将一个表的映射类给完整描述出来。
先看类成员与表的属性对应
定义一个类 FieldCloumnDefinition:
public class FieldCloumnDefinition {
private Field field;//成员
private String cloumn;
public FieldCloumnDefinition() {
}
//成员的get, set方法略
@Override
public String toString() {
return field.getName() +"<=>" + cloumn;
}
}
接着再看类与表的映射
定义一个表和类映射关系的类:
元数据,实例都是为反射使用做准备。
接下来,开始扫描xml文件。(如果不清楚我扫描xml文件的方式,可以看这篇文章JAVA & 自建工具类 & xml文件解析工具)第一步:扫描表和类的映射关系
扫描成功后获得表名,与类的具体地址。这时可以将类地址作为键值,将映射关系类作为值。形成映射池。其中给模板类的元数据复制的是setkClass方法,如下:
void setkClass(String className) {
try {
this.modelClass = Class.forName(className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
给表名赋值就不说了。至此,映射关系类,成员已有二,差三。
第二步:扫描内部的成员和列映射
这里其实有两个标签扫描
new XMLParse() {
@Override
public boolean dealElement(Element element, int index) {
String fieldName = element.getAttribute("name");
String cloumnName = element.getAttribute("cloumn");
ctd.setTrueCloumnName(fieldName, cloumnName);
return true;
}
}.parseElement(element, "field");
第一个是对“field”标签扫描。
void setTrueCloumnName(String fieldName, String cloumnName) {
Field[] fields = this.modelClass.getDeclaredFields();//利用反射获得所有成员
if (fields.length <= 0) {
return;
}
for (Field field : fields) {
if (fieldName.equals(field.getName())) {//匹配成员名,将映射的成员与列类完成
FieldCloumnDefinition fcd = new FieldCloumnDefinition();
fcd.setCloumn(cloumnName);
fcd.setField(field);
this.fDefinitions.add(fcd);
}
}
}
至此,又多了个成员完成,且获得了完整的FieldCloumnDefinition类。
new XMLParse() {
@Override
public boolean dealElement(Element element, int index) {
String keyName = element.getAttribute("name");
ctd.setKeyCloumn(keyName);
return false;
}
}.parseElement(element, "key");
第二个是对“key”扫描
void setKeyCloumn(String keyName) {
if (this.fDefinitions.size() == 0) {
return;
}
for (FieldCloumnDefinition fd : this.fDefinitions) {
if(keyName.equals(fd.getCloumn())) {//主键名和已形成的成员列映射类的列名相同
this.primaryKey = fd;
}
}
}
到此,除了object成员,其他成员都已经有了。而object只有get,set方法。
那么,前三个类都已经完成了。分别是类与表的映射,成员与列名映射,扫描xml文件形成映射池
只剩下最后的ORM类。
点击链接,继续ORM构造吧。