【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,如需轉載請自行聯系原作者