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的存储结构

时间: 2024-10-06 13:57:03

hibernate源码-配置文件加载过程分析的相关文章

看看Spring的源码——Bean加载过程

最近几天跟同事聊起Spring的一些问题,对一些地方有些疑问,趁这两天有点空,看看Spring的源码,了解下具体的实现细节.本文基于Spring 4.0.5版本. 首先Web项目使用Spring是通过在web.xml里面配置org.springframework.web.context.ContextLoaderListener初始化IOC容器的. <listener> <listener-class>org.springframework.web.context.ContextL

深入理解 spring 容器,源码分析加载过程

Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC源码分析介绍它的核心实现原理. Tomcat服务器启动入口文件是web.xml,通过在其中配置相关的Listener和Servlet即可加载Spring MVC所需数据.基于Spring MVC最简单的配置如下. <!-- 加载Spring配置文件 --> <context-param>

SDWebImage 源码分析 --加载gif图片

n年关了,马上放假,终于把手头上的事情告一段落,连续发布了3个app,我也是醉了. 终于有了点时间.想研究下SDWebImage是怎么加载gif图片的. 一直很好奇. 现在开始. 1,首先我们看下SDWebImage是怎么加载gif的. faceButton.image = [UIImage sd_animatedGIFNamed:[NSString stringWithFormat:@"CHATA_%d",i - 46]]; sd_animatedGIFNamed是SDWebImag

Android布局文件的加载过程分析:Activity.setContentView()源码分析

大家都知道在Activity的onCreate()中调用Activity.setContent()方法可以加载布局文件以设置该Activity的显示界面.本文将从setContentView()的源码谈起,分析布局文件加载所涉及到的调用链.本文所用的源码为android-19. Step 1  .Activity.setContentView(intresId) public void setContentView(int layoutResID) { getWindow().setConten

saltstack源码-启动3-config.py配置文件加载

#目标文件位置/usr/lib/python2.6/site-packages/salt/config.py#这个文件加载配置文件的模块.master和minion的配置文件加载都是在这个模块里面完成的#master的启动在这模块里面只涉及到方法和属性只有几个 master和minion的默认配置属性也在这个文件里面定义 DEFAULT_MASTER_OPTS = { 'interface': '0.0.0.0', 'publish_port': '4505', 'pub_hwm': 1000,

转:A10/A20 Bootloader加载过程分析

来自:http://blog.csdn.net/allen6268198/article/details/12905425 A10/A20 Bootloader加载过程分析 注:由于全志A10和A20在加载Bootloader过程方面基本一致,下面仅以A20叙述,但同时也适用于A10.另外在不需要区分Cubieboard1和Cubieboard2的情况下,统称为Cubieboard:另现在市面上一般所说的SD卡即为Micro SD Card,也就是TF卡,为区别于一般传统的SD卡,本文一般使用T

转 A10/A20 Bootloader加载过程分析

A10/A20 Bootloader加载过程分析 注:由于全志A10和A20在加载Bootloader过程方面基本一致,下面仅以A20叙述,但同时也适用于A10.另外在不需要区分Cubieboard1和Cubieboard2的情况下,统称为Cubieboard:另现在市面上一般所说的SD卡即为Micro SD Card,也就是TF卡,为区别于一般传统的SD卡,本文一般使用TF卡描述,但同于平时所说的SD卡. A20的启动过程大概可分为5步:Boot ROM,SPL,Uboot,Kernel,Ro

hibernate延迟加载(懒加载)详解

Hibernae 的延迟加载是一个非常常用的技术,实体的集合属性默认会被延迟加载,实体所关联的实体默认也会被延迟加载.Hibernate 通过这种延迟加载来降低系统的内存开销,从而保证 Hibernate 的运行性能. 下面先来剖析 Hibernate 延迟加载的"秘密". 集合属性的延迟加载 当 Hibernate 从数据库中初始化某个持久化实体时,该实体的集合属性是否随持久化类一起初始化呢?如果集合属性里包含十万,甚至百万的记录,在初始化持久化实体的同时, 完成所有集合属性的抓取,

深入vue - 源码目录及构建过程分析

 公众号原文链接:深入vue - 源码目录及构建过程分析   喜欢本文可以扫描下方二维码关注我的公众号 「前端小苑」 ?“ 本文主要梳理一下vue代码的目录,以及vue代码构建流程,旨在对vue源码整体有一个认知,有助于后续对源码的阅读.” 一.目录结构 上图是对vue的代码的所有目录进行的梳理,其中源码位于src目录下,下面对src下的目录进行介绍. compiler 该目录是编译相关的代码,即将 template 模板转化成 render 函数的代码. vue 提供了 render 函数,r