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