【Hibernate】之模拟Hibernate持久化操作
使用过Hibernate,大家都知道,由于其面向对象的设计,用起来非常方便,且具有很好的跨数据库性,那么Hibernate的底层是怎么实现的呢?其实也就是将对象模型转化为关系模型,最终还是得sql语句来执行。
看过Hibernate源码的同学应该发现, Hibernate底层的核心是代理和反射,那么由此这样我们就可以理解为什么使用Hibernate在效率上始终是致命的。
Ok,下面是一个简单的模拟Hibernate-ORM的save()方法,来管中窥豹,略了解一下Hibernate的底层实现。其中这里我主要演示Hibernate是如何使用反射获取数据,类型等,到这里不得不提,广大Java从业者,想在Java技术道路上有所突破,Java的反射机制,是必须掌握的。
首先模拟出新建一个Session实体类,模拟一个save()方法
为了方便,我这边就不写配置文件了,如果各位想写,可以参考Hibernate的
1
2
3
|
Configuration cfg=
new
Configuration();
cfg.configure();
sessionFactory=cfg.buildSessionFactory();
|
将Hiberane的源码导进去,自己看一下它如何利用dom4j解析实现的。
首先创建一个实体类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package
csg.hibernate.entity;
/**
*
* @author Caesar.hu
* @Date 2014-11-28
* @Time 上午09:32:08
* @TODO
*/
public
class
Student {
private
Integer id;
private
String name;
private
Integer age;
public
Integer getId() {
return
id;
}
public
void
setId(Integer id) {
this
.id = id;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
Integer getAge() {
return
age;
}
public
void
setAge(Integer age) {
this
.age = age;
}
}
|
看代码:然后我们模拟一个Session类,里面构造一个save()方法,
下面这段代码,就是核心,每行代码的注释已经写清楚
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
package
csg.hibernate.entity;
import
java.lang.reflect.Method;
import
java.sql.Connection;
import
java.sql.DriverManager;
import
java.sql.PreparedStatement;
import
java.util.HashMap;
import
java.util.Map;
/**
*
* @author Caesar.hu
* @Date 2014-11-27
* @Time 下午06:02:05
* @TODO模拟一个Session
*/
public
class
Session {
// 1、写好一个表名,这个表名,理论上应该在Student.hbm.xml中读出来,或者是注解JPA映射
// 我这里直接写出来,意思就是说,数据存到数据库的表对应的就是这个
String tableName =
"_student"
;
// 2、为什么创建一个String类型的Map?
// 这个map存放的就是实体的字段属性和数据库字段匹配,本身也是配置在文件中
Map<String, String> cfs =
new
HashMap<String, String>();
String[] methodNames;
// new一个空集合主要是方便反射使用
public
Session() {
cfs.put(
"_id"
,
"id"
);
cfs.put(
"_name"
,
"name"
);
cfs.put(
"_age"
,
"age"
);
methodNames =
new
String[cfs.size()];
}
public
void
save(Student s)
throws
Exception {
// 3、创建SQL语句
String sql = createSQL();
String url =
"jdbc:mysql://localhost/hibernate"
;
String username =
"root"
;
String password =
"root"
;
Class.forName(
"com.mysql.jdbc.Driver"
);
Connection conn = DriverManager.getConnection(url, username, password);
PreparedStatement ps = conn.prepareStatement(sql);
// 9,sql写好之后,这里是是不是需要设置?
// 类似ps.setName(1,s.getId);
// ps.setInterge(2,s.getAge)
//怎么设置?你怎么知道传进来的是什么类型呢?所以这里最重要的是需要用到反射类型
for
(
int
i =
0
; i < methodNames.length; i++) {
System.out.println(methodNames[i]);
// 首先取到存进到数据中的getAge getInt
//10、 通过反射机制根据实体的方法反射出实体一系列的方法getAge,getName,getId和返回类型Integer,String,Integer
Method m = s.getClass().getMethod(methodNames[i]);
System.out.println(m);
System.out.println(s.getClass() +
"-------"
+ m +
"---------"
+ m.getName() +
"-----"
+ m.getReturnType());
//11、获取数组里面getAge,getInt,getName 的返回类型
Class r = m.getReturnType();
//12、根据返回类型判断其应该ps.setString,还是ps.setInteger
if
(r.getName().equals(
"java.lang.String"
)) {
//13、invoke是反射里面的一个方法,其作用是通过类的返回值类型反射出属性值
//14、getAge.invoke(s);同样也可以通过值反射出返回类型
String returnValue = (String) m.invoke(s);
System.out.println(returnValue);
ps.setString(i +
1
, returnValue);
}
//15、同样如果判断是Integer类型,就ps.setInteger
if
(r.getName().equals(
"java.lang.Integer"
)) {
System.out.println(m.invoke(s));
Integer returnValue = (Integer) m.invoke(s);
// System.out.println(returnValue);
ps.setInt(i +
1
, returnValue);
}
}
// ps.executeUpdate();
ps.close();
conn.close();
}
private
String createSQL() {
String str1 =
""
;
int
index =
0
;
for
(String s : cfs.keySet()) {
// 4、通过Map中的key得到Value
String v = cfs.get(s);
// 5、根据get,set方法我们可以知道字段首字母是需要大写的,
// 6、这段代码就是将取出来的value首字母大写加上get
v = Character.toUpperCase(v.charAt(
0
)) + v.substring(
1
);
// 7、这样new出来的空集合里面就放上了getId,getName,getAge
methodNames[index] =
"get"
+ v;
str1 += s +
","
;
index++;
}
str1 = str1.substring(
0
, str1.length() -
1
);
String str2 =
""
;
for
(
int
i =
0
; i < cfs.size(); i++) {
str2 +=
"?,"
;
}
str2 = str2.substring(
0
, str2.length() -
1
);
String sql =
"insert into "
+ tableName +
"("
+ str1 +
")"
+
"values("
+ str2 +
")"
;
// 8、这段sql==
// insert into _table(id,name,age)values(?,?,?);
return
sql;
}
}
|
最后测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package
JunitTest;
import
csg.hibernate.entity.Session;
import
csg.hibernate.entity.Student;
/**
*
* @author Caesar.hu
* @Date 2014-11-28
* @Time 上午09:32:15
* @TODO
*/
public
class
Test {
public
static
void
main(String[] args)
throws
Exception{
Student student=
new
Student();
Session s=
new
Session();
student.setId(
1
);
student.setAge(
12
);
student.setName(
"张三"
);
s.save(student);
}
}
|
Ok,到这里,大家应该能明白,为什么说Hibernate是JDBC的封装?尤其是在考虑web性能的问题上为什么Hibernate不能满足开发需求,反射机制对于Java是多么重要!
其实Hibernate的源码写法也莫不于此,只不过其在底层进行大量的封装,同时为了性能Hibernate也有采用直接将数据生成二进制流进行操作。对于配置文件大家可以看Hibernate 源码是如何利用dom4j解析Hibernate.cfg.xml这个文件,写的尤其经典,Ok?
Java从业者,想在技术上突破,反射机制是必须掌握的!