Hibernate的ORM原理和實現


1.Hibernate和ORM

ORM的全稱是Object Relational Mapping,即對象關系映射。它的實現思想就是將關系數據庫中表的數據映射成為對象,以對象的形式展現,這樣開發人員就可以把對數據庫的操作轉化為對這些對象的操作。因此它的目的是為了方便開發人員以面向對象的思想來實現對數據庫的操作。

2.Hibernate是如何實現映射的

在使用Hibernate實現ORM功能的時候,主要的文件有:映射類(*.java)、映射文件(*.hbm.xml)以及數據庫配置文件(*.properties或*.cfg.xml),它們各自的作用如下:

⑴映射類:它的作用是描述數據庫表的結構,表中的字段在類中被描述成屬性,將來就可以實現把表中的記錄映射成為該類的對象。

⑵映射文件:它的作用是指定數據庫表和映射類之間的關系,包括映射類和數據庫表的對應關系、表字段和類屬性類型的對應關系以及表字段和類屬性名稱的對應關系等。

⑶數據庫配置文件:它的作用是指定與數據庫連接時需要的連接信息,比如連接哪中數據庫、登錄用戶名、登錄密碼以及連接字符串等。

在這三種主要的文件中,映射類為普通Java源文件、映射文件為XML格式、數據庫配置文件為Properties格式或者是XML格式。想理解“映射”首先我們需要知道如何解析這三種文件,即解析XML格式文件、解析Properties格式文件和解析Java類文件。

⑴如何解析XML文件
解析XML的技術可以分為兩類那就是SAX和DOM,這兩種方式有各自的差別和優缺點。實現解析XML文件的功能很方便,我們可以通過下載第三方的一些工具包如xml-apis.jar和xercesImpl.jar等,也可以使用JDK自帶的工具類DocumentBuilderFactory、
DocumentBuilder、Document、Element等等,大家可以通過API文擋查閱這些類的說明。通過這些類我們可以把XML文件的信息讀入內存並通過類中的某些方法獲取指定節點的名字、值、屬性名、屬性值這些信息。

⑵解析Properties文件
Properties文件一般采用“屬性名=屬性值”的形式描述信息。
如果配置文件采用Properties文件描述,我們就需要想辦法解析這種類型的文件了。想解析Properties文件需要熟悉Properties這個類了,這個類有一些常用方法比如,load()加載指定文件並讀取文件中的屬性信息,PropertyNames()返回所有屬性名,getProperty()返回指定屬性名的屬性值。通過解析Properties文件我們可以得到連接數據庫必要的信息,然后通過底層JDBC技術與數據庫建立連接。

⑶解析Java類文件
通過解析映射文件和數據庫配置文件我們可以建立數據庫的連接,可以得到映射類的名字、屬性名、數據庫表名、字段名以及類型等信息。要把數據庫中表的數據映射成為對象,首先需要把表中的記錄取出,然后將每個字段值給映射類對象的每個屬性,這個賦值過程要調用對象中的set方法。
我們現在通過映射文件只知道類名和屬性名,如何根據類名和屬性名調用相應的set和get方法,是一個關鍵問題。在Java中有一種機制叫反射機制,使用這種機制我們可以得到類的信息,包括類只用的修飾符、方法、屬性、繼承的父類以及實現接口等信息。
反射機制相關的類有Class、Field、Method以及Constructor等。通過Class的getFields()、getMethods()和getConstructors()方法得到相應的屬性、方法和構造方法。
通過Field類的getName()、getType()和getModifiers()方法得到相應的屬性名、屬性類型、屬性修飾符信息。
通過Method類getReturnType()可以獲取方法的返回類型,
invoke()方法可以根據給定的方法名和參數值執行對象中對應的方法。
我們可以首先通過以上方法獲取類中的屬性名,然后拼寫成setXXX和getXXX方法名,最后根據方法名執行對應的方法,將數據庫數據加載到對象中。

整理自 Hibernate框架ORM的實現原理

3.模擬Hibernate加載配置文件並映射到實體類的過程

 

(1)創建實體類

/**
 * 示例實體對象
 * 對應學生表
 */
public class Student {

	private int id;
	private String name;
	private String score;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getScore() {
		return score;
	}
	public void setScore(String score) {
		this.score = score;
	}
}

  

(2)模擬解析hbm.xml和數據庫配置文件,並且創建Session

import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

/**
 * 模擬Session類實現
 */
public class Session {
	
	/**
	 * 模擬Stument.hbm.xml文件的內容
	 * 這里沒有解析hbm.xml
	 */
    private Map<String ,String > hbmInfo = new HashMap<String ,String >(); 
    
    //存放數據庫連接配置
    private Map<String, String> conConfig = new HashMap<String, String>();

    //實體的get方法集合
    String methodNames[]; 
    
	public Session(){
		/**
		 * 初始化實體,具體的實現應該是讀取hbm.xml文件解析字段
		 */
		hbmInfo.put("id", "id");  
		hbmInfo.put("name", "name");  
		hbmInfo.put("password", "password");  
        methodNames = new String[hbmInfo.size()];  
	}

	/**
	 * 創建數據庫連接
	 * @return
	 * @throws IOException 
	 * @throws JDOMException 
	 */
	public Connection initConn() throws Exception{
		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);           
          }
        
      //根據配置文件獲得數據庫連接
        Class.forName(conConfig.get("driver"));  
        Connection con = DriverManager.getConnection(conConfig.get("url"),conConfig.get("username"),conConfig.get("password"));
		return con;  
	}
	
	/**
	 * 持久化對象
	 * @param student
	 */
	public void save(Student student) { 
		
		 String sql = getSaveStatement();  
	     System.out.println(sql);  
	  
	     try {
	    	//獲得連接
			Connection con = initConn();
			//創建jdbc執行語句
			PreparedStatement state =  (PreparedStatement) con.prepareStatement(sql);  
			
			for(int i=0;i<methodNames.length;i++) {  

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

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

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

            state.executeUpdate();  
            state.close();  
            con.close();  
		} catch (Exception e) {
			e.printStackTrace();
		}  
	}
	
	
	private String getSaveStatement(){
		//strColumn代表數據庫中表中的屬性列。並將其連接起來。  
        String strColumn = "";  
        int index=0;  
        for(String key :hbmInfo.keySet())  
        {  
            strColumn +=key+",";  
            String v = hbmInfo.get(key);  

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

        //拼接參數占位符,即:(?, ?, ?)
        String strValue = "";  
        for(int i=0;i<hbmInfo.size();i++)  
            strValue +="?,";        
        strValue = strValue.substring(0,strValue.length()-1);  

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


(3)測試持久化

public class Test {

	/**
	 * 測試持久化
	 * 數據庫語句:
	 * DROP TABLE IF EXISTS `tb_student`;  
	 * CREATE TABLE tb_student (
	 * `id` int(10) NOT NULL DEFAULT 0 ,
	 * `name` varchar(50) DEFAULT NULL ,
	 * `score` int(11) DEFAULT 0 ,
	 * PRIMARY KEY (`id`)
	 * )ENGINE=MyISAM  DEFAULT CHARSET=utf8; 
	 */
	public void main(){
		Student student=new Student();
		student.setId(100);
		student.setName("Tom");
		student.setScore("99");
		//獲得Session對象
		Session session=new Session();
		//執行持久化操作
		session.save(student);
	}
	
}

  

 

參考 菜鳥學SSH(十五)——簡單模擬Hibernate實現原理

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM