Hibernate建議,在一個應用系統當中Configuration與SessionFactory爲單例,Session爲多例.
當我們執行如下代碼,hibernate開始加載默認的配置文件
new Configuration().configure()
hibernate會在classath的根路徑下,查找名爲"hibernate.cfg.xml" 的配置文件,並解析它,過程如圖1所示
圖1:配置文件加載過程時序圖
下面一起分析一下配置文件加載過程
Step 1.Configuration.configure()
該方法定義在 org/hibernate/cfg/Configuration.java文件中
public Configuration configure() throws HibernateException { configure( "/hibernate.cfg.xml" ); return this; }
調用重載方法configure( "/hibernate.cfg.xml" ),傳入默認的配置文件名稱作爲實參
Step 2.Configuration.doConfigure
Step 3.Configuration.addProperties該方法定義在 org/hibernate/cfg/Configuration.java文件中
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()與parseSessionFactory
Step 4. Configuration.parseSessionFactory該方法定義在 org/hibernate/cfg/Configuration.java文件中
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 ); }
這個方法主要將配置文件中的property元素的內容解析,存放至properties成員變量當中,properties是一個Properties實例,
在if判斷條件中發現,如果name的值不是以"hibernate"開頭,將自動補全
<hibernate-configuration> <session-factory > <!-- 顯示sql --> <property name="show_sql">true</property> <!-- 數據庫連接配置 --> <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="hibernate.connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property> <property name="hibernate.connection.username">diankun</property> <property name="hibernate.connection.password">diankun</property> <!-- 生成sql語句採用的語法 --> <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property> <!-- 自動創建表 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- bean與表的映射 --> <mapping resource="com/demo/model/Student.hbm.xml"/> <mapping resource="com/demo/model/Certificate.hbm.xml"/> <mapping resource="com/demo/model/User.hbm.xml"/> <mapping resource="com/demo/model/Role.hbm.xml"/> </session-factory> </hibernate-configuration>
該方法定義在 org/hibernate/cfg/Configuration.java文件中
private void parseSessionFactory(Element sfNode, String name) { Iterator elements = sfNode.elementIterator(); while ( elements.hasNext() ) { Element subelement = (Element) elements.next(); String subelementName = subelement.getName(); if ( "mapping".equals( subelementName ) ) { parseMappingElement( subelement, name ); } else if ( "class-cache".equals( subelementName ) ) { ...... } else if ( "collection-cache".equals( subelementName ) ) { ...... } else if ( "listener".equals( subelementName ) ) { parseListener( subelement ); } else if ( "event".equals( subelementName ) ) { parseEvent( subelement ); } } }
形參sfNode指向session-factory元素節點
形參name的爲session-factory的name屬性值,等於null
由於我們在配置文件中,添加了如下配置:
<mapping resource="com/demo/model/Student.hbm.xml"/> <mapping resource="com/demo/model/Certificate.hbm.xml"/> <mapping resource="com/demo/model/User.hbm.xml"/> <mapping resource="com/demo/model/Role.hbm.xml"/>
這時滿足條件爲""mapping".equals( subelementName )" 的分支
Step 5.Configuration.parseMappingElement
Step 6.Configuration.addResource該方法定義在 org/hibernate/cfg/Configuration.java文件中
private void parseMappingElement(Element mappingElement, String name) { final Attribute resourceAttribute = mappingElement.attribute( "resource" ); ...... if ( resourceAttribute != null ) { final String resourceName = resourceAttribute.getValue(); log.debug( "session-factory config [{}] named resource [{}] for mapping", name, resourceName ); addResource( resourceName ); } else if ( fileAttribute != null ) { ...... } else if ( jarAttribute != null ) { ...... } else if ( packageAttribute != null ) { ...... } else if ( classAttribute != null ) { ...... } else { throw new MappingException( "<mapping> element in configuration specifies no known attributes" ); } }
我們在hibernate.cfg.xml中配置 <mapping resource="com/demo/model/User.hbm.xml"/> ,滿足條件 resourceAttribute != null ,調用addResource( resourceName ) 方法
Step 7.Configuration.add該方法定義在 org/hibernate/cfg/Configuration.java文件中
public Configuration addResource(String resourceName) throws MappingException { log.info( "Reading mappings from resource : " + resourceName ); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); InputStream resourceInputStream = null; if ( contextClassLoader != null ) { resourceInputStream = contextClassLoader.getResourceAsStream( resourceName ); } if ( resourceInputStream == null ) { resourceInputStream = Environment.class.getClassLoader().getResourceAsStream( resourceName ); } if ( resourceInputStream == null ) { throw new MappingNotFoundException( "resource", resourceName ); } add( resourceInputStream, "resource", resourceName ); return this; }
這個方法功能比較簡單,通過類加載器來開啓輸入流,並將輸入流作爲實參,轉發給 add( resourceInputStream, "resource", resourceName ) 來處理
Step 8.MetadataSourceQueue.add該方法定義在 org/hibernate/cfg/Configuration.java文件中
public void add(XmlDocument metadataXml) { if ( inSecondPass || !isOrmXml( metadataXml ) ) { metadataSourceQueue.add( metadataXml ); } else { final MetadataProvider metadataProvider = ( (MetadataProviderInjector) reflectionManager ).getMetadataProvider(); JPAMetadataProvider jpaMetadataProvider = ( JPAMetadataProvider ) metadataProvider; List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( metadataXml.getDocumentTree() ); for ( String className : classNames ) { try { metadataSourceQueue.add( reflectionManager.classForName( className, this.getClass() ) ); } catch ( ClassNotFoundException e ) { throw new AnnotationException( "Unable to load class defined in XML: " + className, e ); } } } }
add(InputSource inputSource, String originType, String originName)
add(InputSource inputSource, Origin origin)
以上兩上重載add()方法,調用過程跳過
在add(XmlDocument metadataXml)方法中,我們可以看到它作了一個委託處理,
交給成員變量metadataSourceQueue處理,metadataSourceQueue是一個MetadataSourceQueue實例,是Configuration的內部類
該方法定義在 org/hibernate/cfg/Configuration.java文件中
public void add(XmlDocument metadataXml) { final Document document = metadataXml.getDocumentTree(); final Element hmNode = document.getRootElement(); Attribute packNode = hmNode.attribute( "package" ); String defaultPackage = packNode != null ? packNode.getValue() : ""; Set<String> entityNames = new HashSet<String>(); findClassNames( defaultPackage, hmNode, entityNames ); for ( String entity : entityNames ) { hbmMetadataByEntityNameXRef.put( entity, metadataXml ); } this.hbmMetadataToEntityNamesMap.put( metadataXml, entityNames ); }
metadataXml指向一個文檔根節點
findClassNames( defaultPackage, hmNode, entityNames ) 方法作用:
將實體映射文件中的class元素的name屬性,存儲在局部變量entityNames之中
再將entityNames集合元素的值作爲key,文檔根結點作爲value,存儲在成員變量hbmMetadataByEntityNameXRef中,
hbmMetadataByEntityNameXRef是 Map<String, XmlDocument> 實例
將文檔根結點作爲key,將集合entityNames作爲value,存儲在成員變量hbmMetadataToEntityNamesMap中,
hbmMetadataToEntityNamesMap是 LinkedHashMap<XmlDocument, Set<String>> 的實例,
protected class MetadataSourceQueue implements Serializable { private LinkedHashMap<XmlDocument, Set<String>> hbmMetadataToEntityNamesMap = new LinkedHashMap<XmlDocument, Set<String>>(); private Map<String, XmlDocument> hbmMetadataByEntityNameXRef = new HashMap<String, XmlDocument>(); //XClass are not serializable by default private transient List<XClass> annotatedClasses = new ArrayList<XClass>(); //only used during the secondPhaseCompile pass, hence does not need to be serialized private transient Map<String, XClass> annotatedClassesByEntityNameMap = new HashMap<String, XClass>(); private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ...... } private void writeObject(java.io.ObjectOutputStream out) throws IOException { ...... } public void add(XmlDocument metadataXml) { final Document document = metadataXml.getDocumentTree(); final Element hmNode = document.getRootElement(); Attribute packNode = hmNode.attribute( "package" ); String defaultPackage = packNode != null ? packNode.getValue() : ""; Set<String> entityNames = new HashSet<String>(); findClassNames( defaultPackage, hmNode, entityNames ); for ( String entity : entityNames ) { hbmMetadataByEntityNameXRef.put( entity, metadataXml ); } this.hbmMetadataToEntityNamesMap.put( metadataXml, entityNames ); } }
hbmMetadataByEntityNameXRef與hbmMetadataToEntityNamesMap的存儲結構,如圖2所示:
圖2:兩個Map的存儲結構