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