【Hibernate】之模擬Hibernate持久化操作
使用過Hibernate,大家都知道,由於其面向對象的設計,用起來非常方便,且具有很好的跨數據庫性,那麼Hibernate的底層是怎麼實現的呢?其實也就是將對象模型轉化爲關係模型,最終還是得sql語句來執行。
看過Hibernate源碼的同學應該發現, Hibernate底層的核心是代理和反射,那麼由此這樣我們就可以理解爲什麼使用Hibernate在效率上始終是致命的。
Ok,下面是一個簡單的模擬Hibernate-ORM的save()方法,來管中窺豹,略瞭解一下Hibernate的底層實現。其中這裏我主要演示Hibernate是如何使用反射獲取數據,類型等,到這裏不得不提,廣大Java從業者,想在Java技術道路上有所突破,Java的反射機制,是必須掌握的。
首先模擬出新建一個Session實體類,模擬一個save()方法
爲了方便,我這邊就不寫配置文件了,如果各位想寫,可以參考Hibernate的
Configuration cfg=new Configuration(); cfg.configure(); sessionFactory=cfg.buildSessionFactory();
將Hiberane的源碼導進去,自己看一下它如何利用dom4j解析實現的。
首先創建一個實體類:
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()方法,
下面這段代碼,就是核心,每行代碼的註釋已經寫清楚
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; } }
最後測試代碼:
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從業者,想在技術上突破,反射機制是必須掌握的!