SSH 簡單模擬Hibernate實(shí)現(xiàn)原理

2018-09-28 18:56 更新

簡單模擬Hibernate實(shí)現(xiàn)原理

之前寫了Spring的實(shí)現(xiàn)原理,今天我們接著聊聊Hibernate的實(shí)現(xiàn)原理,這篇文章只是簡單的模擬一下Hibernate的原理,主要是模擬了一下Hibernate的Session類。好了,廢話不多說,先看看我們的代碼:

package com.tgb.hibernate;  

import java.lang.reflect.Method;  
import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.PreparedStatement;  
import java.sql.SQLException;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  

import org.jdom.Document;  
import org.jdom.Element;  
import org.jdom.input.SAXBuilder;  
import org.jdom.xpath.XPath;  

import com.tgb.hibernate.model.User;  

public class Session {  

    //表名  
    String tableName = "user";    

    //存放數(shù)據(jù)庫連接配置  
    private Map<String, String> conConfig = new HashMap<String, String>();  

    //存放實(shí)體屬性  
    private Map<String ,String > columns = new HashMap<String ,String >();    

    //實(shí)體的get方法集合  
    String methodNames[];    

    public Session () {    

        //初始化實(shí)體,這里就不用讀取配置文件的方式了,有點(diǎn)麻煩。   
        columns.put("id", "id");    
        columns.put("name", "name");    
        columns.put("password", "password");    
        methodNames = new String[columns.size()];    

    }    

    /** 
     * 創(chuàng)建數(shù)據(jù)庫連接 
     * @return 
     * @throws Exception 
     */  
    public Connection createConnection() throws Exception {  
         //解析xml文件,讀取數(shù)據(jù)庫連接配置        
        SAXBuilder sb = new SAXBuilder();    
        Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream("hibernate.cfg.xml"));    
        Element root = doc.getRootElement();    
        List list = XPath.selectNodes(root, "/hibernate-configuration/property");    

        for (int i = 0; i < list.size(); i++) {                
           Element property = (Element) list.get(i);    
           String name = property.getAttributeValue("name");    
           String value = property.getText();            
           conConfig.put(name, value);           
          }    

        //根據(jù)配置文件獲得數(shù)據(jù)庫連接  
        Class.forName(conConfig.get("driver"));    
        Connection con = DriverManager.getConnection(conConfig.get("url"),conConfig.get("username"),conConfig.get("password"));    

        return con;  
    }  

    /** 
     * save方法,持久化對(duì)象 
     * @param user 
     */  
    public void save(User user) {    

        String sql = createSql();    
        System.out.println(sql);    

        try {    
            Connection con = createConnection();    
            PreparedStatement state =  (PreparedStatement) con.prepareStatement(sql);    

            for(int i=0;i<methodNames.length;i++) {    

                //得到每一個(gè)方法的對(duì)象    
                Method method = user.getClass().getMethod(methodNames[i]);    

                //得到他的返回類型    
                Class cla = method.getReturnType();    

                //根據(jù)返回類型來設(shè)置插入數(shù)據(jù)庫中的每個(gè)屬性值。    
                if(cla.getName().equals("java.lang.String")) {    
                    String returnValue = (String)method.invoke(user);    
                    state.setString(i+1, returnValue);    
                }    
                else if(cla.getName().equals("int")) {    
                    Integer returnValue = (Integer) method.invoke(user);    
                    state.setInt(i+1, returnValue);    
                }                       
            }    

            state.executeUpdate();    
            state.close();    
            con.close();    

        } catch (ClassNotFoundException e) {        
            e.printStackTrace();    
        } catch (SQLException e) {    
            e.printStackTrace();    
        } catch (Exception e) {    
            e.printStackTrace();    
        }     
    }    

    /**  
     * 得到sql語句  
     * @return 返回sql語句  
     */    
    private String createSql() {    

        //strColumn代表數(shù)據(jù)庫中表中的屬性列。并將其連接起來。    
        String strColumn = "";    
        int index=0;    
        for(String key :columns.keySet())    
        {    
            strColumn +=key+",";    
            String v = columns.get(key);    

            //獲得屬性的get方法,需要將屬性第一個(gè)字母大寫如:getId()  
            v = "get" + Character.toUpperCase(v.charAt(0)) + v.substring(1);    
            methodNames[index] = v;   
            index++;    
        }    
        strColumn  = strColumn.substring(0, strColumn.length()-1);    

        //拼接參數(shù)占位符,即:(?, ?, ?)  
        String strValue = "";    
        for(int i=0;i<columns.size();i++)    
            strValue +="?,";    

        strValue = strValue.substring(0,strValue.length()-1);    

        String sql = "insert into " + tableName +"(" + strColumn + ")" + " values (" + strValue + ")";     
        return sql;    
    }    
}  

以上代碼主要是完成了Hibernate的save()方法,該類有一個(gè)構(gòu)造方法,一個(gè)構(gòu)建sql語句的方法,一個(gè)獲得數(shù)據(jù)庫連接的方法。最后通過save()方法結(jié)合前面幾個(gè)方法獲得結(jié)果,將實(shí)體對(duì)象持久化到數(shù)據(jù)庫。

基本原理就是:首先,獲得數(shù)據(jù)庫連接的基本信息;然后,獲得實(shí)體的映射信息;接著,也是最關(guān)鍵的步驟,根據(jù)前面獲得的信息,組裝出各種sql語句(本例只有簡單的insert),將實(shí)體按照不同的要求查找或更新(增、刪、改)到數(shù)據(jù)庫。

當(dāng)然Hibernate的具體實(shí)現(xiàn)遠(yuǎn)沒有這么簡單,Hibernate中大量運(yùn)用了cglib的動(dòng)態(tài)代理,其中l(wèi)oad()方法就是一個(gè)例子。大家都知道,調(diào)用load()方法是Hibernate不會(huì)向數(shù)據(jù)庫發(fā)sql語句,load()方法得到的是目標(biāo)實(shí)體的一個(gè)代理類,等到真正用到實(shí)體對(duì)象的時(shí)候才會(huì)去數(shù)據(jù)庫查詢。這也是Hibernate的一種懶加載的實(shí)現(xiàn)方式。

總結(jié)一句話,這些框架之所以能夠做到靈活,就是因?yàn)樗鼈兌己芎玫睦昧藨屑虞d機(jī)制,在運(yùn)行期在確定實(shí)例化誰,需要誰實(shí)例化誰,什么時(shí)候需要,什么時(shí)候?qū)嵗?。這樣設(shè)計(jì)出來能不靈活嗎?這些思想值得我們好好研究,并運(yùn)用到我們的設(shè)計(jì)中去。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)