Configuration接口的做用是對Hibernate進行配置 以及對他進行啓動 在Hibernate的啓動過程當中 Configuration類的實例首先定位映射文檔的位置 讀取這些配置 而後建立一個SessionFactory對象java
一個org.hibernate.cfg.Configuration實例表明了一個應用程序中Java類型到SQL數據庫映射的完整集合。Configuration被用來構建一個不可變的SessionFactory,映射定義則由不一樣的XML映射定義文件編譯而來。node
Configuration有如下幾個方面的操做函數數據庫
1 爲Configuration指定映射文件編程
你能夠直接實例化Configuration來獲取一個實例 併爲他指定XML映射定義文件 若是映射定義文件在類路徑中 請使用addResource()設計模式
Configuration cfg = new Configuration().addResource("com/demo/hibernate/beans/User.hbm.xml");
2 爲Configuration指定持久化類緩存
一個替代的方法是指定被映射的類 讓hibernate幫你尋找映射定義文件安全
Configuration cfg = new Configuration().addClass(com.demo.hibernate.beans.User.class);
Hibernate將會在類路徑中需找名字爲 /com/demo/hibernate/beans/User.hbm.xml 映射定義文件 消除了任何對文件名的硬編譯服務器
3 爲Configuration指定配置屬性 Configuration也容許指定配置屬性session
Configuration cfg =new Configuration().addClass(com.demo.hibernate.beans.User.class) .setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect") .setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test") .setProperty("hibernate.order_update","true");
4 Configuration的三種加載方式app
在Hibernate的啓動與開發流程中 要使用一個Configuration 須要爲他設置三個方面的內容
數據庫鏈接屬性
hbm.xml文件 / POJO類
其中 第二個和第三個只須要設置一個 就會自動需找另外一個 由於這二者只需一個
第一種方式是使用hibernate.cfg.xml 該文件設置了數據庫鏈接的屬性和hbm.xml映射文件配置 hibernate會自動加載該配置屬性 並自動找到POJO 所以要取得Configuration對象 只須要簡單的建立改對象便可
Configuration cfg = new Configuration(); cfg.configuration("hibernate.cfg.xml");
第二種方式是經過hibernate.properties 省略
第三種方式是徹底在構造時進行硬編碼設置 設置過程以下所示
Configuration cfg =new Configuration() .addClass(com.demo.hibernate.beans.User.class) .setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect") .setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test") .setProperty("hibernate.order_update","true");
第一種方式是咱們最經常使用的方式
咱們在獲取SessionFactory的時候,第一個語句就是:
Configuration configuration = new Configuration();
查看源碼可知,Configuration類的公共構造方法只有一個,而且是無參數的:
public Configuration() { this( new SettingsFactory() ); }
這個構造方法調用了一個受保護的構造方法:
protected Configuration(SettingsFactory settingsFactory) { this.settingsFactory = settingsFactory; reset(); }
受保護的構造方法裏調用了私有方法reset,reset方法裏有一句這樣的代碼:
properties = Environment.getProperties();
這句代碼會從Hibernate環境裏去獲取hibernate配置,查看Environment類的方法getProperties:
public static Properties getProperties() { Properties copy = new Properties(); copy.putAll(GLOBAL_PROPERTIES); return copy; }
這個方法獲得的是全局配置屬性的副本。再看看全局屬性GLOBAL_PROPERTIES是如何初始化的。Environment的靜態代碼塊裏有以下代碼:
InputStream stream = ConfigHelper.getResourceAsStream("/hibernate.properties"); try { GLOBAL_PROPERTIES.load(stream); log.info( "loaded properties from resource hibernate.properties: " + PropertiesHelper.maskOut(GLOBAL_PROPERTIES, PASS) ); }
代碼跟蹤到這裏,咱們能夠知道hibernate首先必定會加載屬性配置文件hibernate.properties,並且此文件的路徑是寫死的。
若是用戶想用XML配置hibernate,就須要編寫以下代碼:
(properties 是直接用構造方法 new Configuration(),這裏是生成後調用configure())
configuration.configure();
configure有五個重載方法:
看一下缺省的重載方法:
public Configuration configure() throws HibernateException { configure( "/hibernate.cfg.xml" ); return this; }
它調用的是另外一個重載方法,加載的配置文件是寫死的。這五個方法最後調用同一個方法doConfigure(Document doc):
protected Configuration doConfigure(Document doc) throws HibernateException { Element sfNode = doc.getRootElement().element( "session-factory" ); String name = sfNode.attributeValue( "name" ); if ( name != null ) { properties.setProperty( Environment.SESSION_FACTORY_NAME, name ); } addProperties( sfNode ); parseSessionFactory( sfNode, name ); Element secNode = doc.getRootElement().element( "security" ); if ( secNode != null ) { parseSecurity( secNode ); } log.info( "Configured SessionFactory: " + name ); log.debug( "properties: " + properties ); return this; }
看一下方法addProperties:
private void addProperties(Element parent) { Iterator itr = parent.elementIterator( "property" ); while ( itr.hasNext() ) { Element node = (Element) itr.next(); String name = node.attributeValue( "name" ); String value = node.getText().trim(); log.debug( name + "=" + value ); properties.setProperty( name, value ); if ( !name.startsWith( "hibernate" ) ) { properties.setProperty( "hibernate." + name, value ); } } Environment.verifyProperties( properties );
能夠看到,若是配置屬性名不是以「hibernate」開頭會自動加上「hibernate.」,這就是爲何,咱們在用XML配置hibernate的時候,屬性名「hibernate.」能夠省去,可是在使用屬性文件或者編程方式配置時,「hibernate.」是不能省掉的。
hibernate加載配置的順序是:properties配置——》XML配置或者編程方式配置。至因而先加載XML配置仍是編程方式的配置,就要看用戶的語句順序了,可是有一點是肯定的:後加載的配置會覆蓋先加載的配置。
最後咱們也能夠看到,hibernate將基本的配置信息(不包括實體映射信息)保存到了configuration的properties成員屬性中。
獲取sessionFactory:
SessionFactory sessionFactory = configuration.buildSessionFactory();
SessionFactory在Hibernate中實際上起到了一個緩衝區的做用 他緩衝了HIbernate自動生成SQL語句和其餘的映射數據 還緩衝了一些未來有可能重複利用的數據
爲了能建立一個SessionFactory對象 應該在Hibernate初始化的時候建立一個Configuration類的實例 並將已經寫好的映射文件交給他處理 這樣Configuration對象就能夠建立一個SessionFactory對象 當SessionFactory對象建立成功後 Configuration對象就沒用用了 就能夠簡單的拋棄他
示例代碼:
Configuration cfg = new Configuration(); cfg.addResource("com/demo/hibernate/beans/User.hbm.xml"); cfg.setProperty(System.getProperties()); SessionFactory sessionFactory = cfg.buildSessionFactory();
SessionFactory用到了一個設計模式 工廠模式 用戶程序從工程類SessionFactory取得Session實例 設計者的意圖就是讓它能在整個應用中共享 典型的來講 一個項目一般只須要一個SessionFactory就夠了 所以咱們就設計了HibernateSessionFactory.Java這個輔助類 定義了一個靜態的Configuration和SessionFactory對象
private static final Configuration cfg = new Configuration(); private static org.hibernate.SessionFactory sessionFactory;
這兩個對象對整個應用來講只有一個實例存在 所以爲用戶的訪問定義一個本地線程變量:
該線程變量是靜態的 對每個訪問該線程的用戶產生一個實例 這樣在要取得Session對象時 首先從當前用戶的線程中取得Session對象 若是尚未建立 則從SessionFactory中建立一個Session 此時會判斷SessionFactory對象是否已經建立 該對象對這個應用來講 只有一個 所以 只有第一次訪問該變量的用戶纔會建立該對象
HibernateSessionFactory.java 取得Session對象的過程以下表示
public static Session currentSession() throws HibernateException { Session session = (Session) threadLocal.get(); if (session == null) { if (sessionFactory == null) { try { cfg.configure(CONFIG_FILE_LOCATION); sessionFactory = cfg.buildSessionFactory(); } catch (Exception e) { System.err.println("%%%% Error Creating SessionFactory %%%%"); e.printStackTrace(); } } session = sessionFactory.openSession(); threadLocal.set(session); } return session; }
首先判斷threadLocal中是否存在Session對象 若是不存在 則建立Session對象 在建立Session對象時 首先要判斷系統是否已經加載Configuration 若是沒有sessionFactory 則須要先建立該對象 建立完成的Session對象 須要保存在threadLocal中以供本次訪問線程的下一次調用
在關閉Session對象是 只須要從當前線程中取得Session對象 關閉該對象 並置空本地線程變量便可
public static void closeSession() throws HibernateException { Session session = (Session) threadLocal.get(); threadLocal.set(null); if (session != null) { session.close(); } }
Hibernate做爲持久成中間件,它的具體實現對與上層調用是透明的,即上層經過接口來調用Hibernate的具體實現,因此對於入門級別的討論來講,天然應該先從接口開始了。
全部的Hibernate應用都會訪問它的5個核心接口,分別以下:
Configuration接口:
SessionFactory接口:
Session接口:
Transaction接口:
Query和Criteria接口:
----------------------------------
分別簡單介紹一下:
一、Configuration接口
Configuration用於配置並啓動Hibernate。Hibernate應用經過Configuration的實例來指定對象-關係映射文件,或經過Configuration動態配置Hibernate的屬性,而後經過Configuration來建立相應的SessionFactory實例。
二、SessionFactory接口
一個SessionFactory對應一個數據源,它是個重量級對象,不可隨意生成多個實例。對於通常的單數據庫應用來講,只須要一個SessionFactory就足夠了。固然若是有多個數據庫的話,仍是須要爲每一個數據庫生成對應的SessionFactory。它是線程安全的,同一個實例能夠被應用中的多個線程共享。
也許你會很好奇,SessionFactory爲何是重量級對象呢?我也一樣好奇,經過查看Hibernate的源碼,發現SessionFactory存放了大量預約義的SQL語句以及映射元數據,因此天然須要很大的緩存了,同時須要必定的CPU時間來計算生成。想一想Hibernate的這個設計是頗有意義的,由於有了Mapping文件,不少SQL語句就已經肯定了,只須要動態生成一次就能夠了,這個設計也是爲了提升持久化的效率。
三、Session接口
從SessionFactory中能夠得到Session實例。
Session接口是Hibernate應用中使用最普遍的接口了,它是持久化管理器,提供添加、更新、刪除、加載、查詢對象。Session不是線程安全的,因此應避免多個線程共享同一個Session實例。Session是輕量級對象,它的建立和銷燬不須要太多資源,這意味着在應用中能夠常常建立和銷燬Session對象。
Session有一個緩存,稱之爲Hibernate的一級緩存,它存放當前工做單元加載的持久化對象,每一個Session都有本身的緩存,緩存中的對象只能被當前工做單元訪問。
四、Transaction接口
Transaction是Hibernate的數據庫事務接口,它對底層道德事務接口進行了封裝,底層事務接口包括:
JDBC API
JTA(Java Transaction API)
CORBA(Common Object Requet Broker Architecture) API
Hibernate應用能夠經過一致Transaction接口來聲明事務邊界,這有助於應用能夠在不一樣的環境或容器中移植。具體的事務實現使用在Hibernate.properties中進行指定。
五、Query和Criteria接口
這兩個是Hibernate的查詢接口,用於向數據庫查詢對象,以及控制執行查詢的過程。Query實例包裝了一個HQL(Hibernate Query Language)來查詢。Criteria接口徹底封裝了基於字符串形式的查詢語句,比Query更面向對象,Criteria更擅長執行動態查詢。
Hibernate是如何初始化鏈接池的呢?先看下圖:
Hibernate是如何初始化鏈接池的呢?先看下圖:
1.configuration實例化的時候,hibernate會去讀取配置信息,而且將基本的配置信息(不包括實體映射信息)保存到configuration的字段properties中。
2.調用configuration的buildSessionFactory()方法,buildSessionFactory()方法又會調用SettingFactory的buildSettings(Properties props)方法。
3.buildSettings方法調用ConnectionProviderFactory的newConnectionProvider(Properties props)方法產生一個ConnectionProvider對象。
4.buildSettings方法實例化一個Setting對象,並將ConnectionProvider傳遞給Setting。
5.buildSettings返回一個Setting對象,而後buildSessionFactory()方法會實例化一個SessionFactoryImpl,而且將Setting對象傳給SessionFactoryImpl,最後返回SessionFactoryImpl。
configuration的buildSessionFactory()方法其實是實例化了一個鏈接池,而且把這個鏈接池交給SessionFactory管理。
ConnctionProvider是鏈接提供者,hibernate的數據庫鏈接都來自它。當咱們調用SessionFactoryImpl的openSession()方法時,就很容易得到數據庫鏈接了。
Hibernate爲ConnctionProvider提供瞭如下幾個實現類:
1.C3P0ConnectionProvider。從C3P0鏈接池中獲取數據庫鏈接。
2.ProxoolConnectionProvider。從Proxool鏈接池中獲取數據庫鏈接。
3.DatasourceConnectionProvider。通常是經過JNDI從應用服務器(如JBoss)中獲取數據源。
4.DriverManagerConnectionProvider。Hibernate自帶的鏈接池。
5.UserSuppliedConnectionProvider。沒有任何實現,只是拋出了異常。
到底使用哪個呢?看看ConnectionProviderFactory.newConnectionProvider(Properties props)方法就知道了:
public static ConnectionProvider newConnectionProvider(Properties properties, Map connectionProviderInjectionData) throws HibernateException { ConnectionProvider connections; String providerClass = properties.getProperty( Environment.CONNECTION_PROVIDER );//hibernate.connection.provider_class if ( providerClass != null ) { connections = initializeConnectionProviderFromConfig( providerClass ); } else if ( c3p0ConfigDefined( properties ) && c3p0ProviderPresent() ) { connections = initializeConnectionProviderFromConfig("org.hibernate.connection.C3P0ConnectionProvider"); } else if ( properties.getProperty( Environment.DATASOURCE ) != null ) {//hibernate.connection.datasource connections = new DatasourceConnectionProvider(); } else if ( properties.getProperty( Environment.URL ) != null ) {//hibernate.connection.url connections = new DriverManagerConnectionProvider(); } else { connections = new UserSuppliedConnectionProvider(); } ...... connections.configure( properties ); return connections; }
上面是核心代碼。
1.若是配置了hibernate.connection.provider_class屬性,就會根據指定的類去實例化。屬性值爲C3P0ConnectionProvider或者ProxoolConnectionProvider的全限定名。
2.若是配置了hibernate.connection.datasource屬性,則會實例化一個DatasourceConnectionProvider。屬性值爲JNDI名稱。
3.若是上面兩個都沒配置,則會去判斷是否配置hibernate.connection.url屬性。若是配置了,會實例化一個DriverManagerConnectionProvider。
4.若是沒有配置hibernate.connection.url屬性,則實例化一個UserSuppliedConnectionProvider,其實是拋出了異常。
咱們也能夠本身寫一個ConnctionProvider的實現,而後配置hibernate.connection.provider_class屬性,屬性值爲咱們本身的實現類的全限定名。
從上圖能夠看出,session有兩種方法獲取數據庫鏈接:
1.session.getJDBCContext().getConnectionManager().getConnection();
2.((SessionFactoryImplementor)session.getSessionFactory()).getConnectionProvider().getConnection();
不管是使用哪種方法,最終都是從sessionFactory的Settings中獲取ConnectionProvider,而後再從ConnectionProvider獲取connection。