hibernate源碼-配置文件加載過程分析

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

該方法定義在 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 3.Configuration.addProperties

該方法定義在 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>


Step 4. Configuration.parseSessionFactory

該方法定義在 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

該方法定義在 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 6.Configuration.addResource

該方法定義在 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 7.Configuration.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的內部類


Step 8.MetadataSourceQueue.add

該方法定義在 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的存儲結構



發佈了142 篇原創文章 · 獲贊 11 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章