Hibernate-04-核心Configuration

本文详细剖析了Hibernate配置过程,包括配置文件的读取、解析及如何将配置信息映射到内部数据结构。通过源码分析,介绍了Configuration类的核心作用及其与SessionFactory的创建流程。

上一篇:         下一篇:

 

一切从配置开始吧!

对于Hibernate,最重要的配置文件当然是:hibernate.cfg.xml

 

先分析一下这个配置文件吧,hibernate-configuration-3.0.dtd这个不要忘记看了,虽然人家只是格式定制而已,OK!开始Configuration之旅吧。

 

Configuration 继承 Serializable,表示这类是可序列化的。好了,不扯啦。4000多行的代码,如果一行一行的看,人都困了,睡意三二。

 

我决定不从构造函数说起,请原谅我一向的风格。

从使用说起,

Configuration conf=new Configuration();

SessionFactory factory=conf.configure().buildSessionFactory();

 

configure()这个方法就是入口。

方法1

public Configuration configure() throws HibernateException {
  configure( "/hibernate.cfg.xml" );
  return this;
}

 

方法2

 

public Configuration configure(String resource) throws HibernateException {
  log.info( "configuring from resource: " + resource );
  InputStream stream = getConfigurationInputStream( resource );
  return doConfigure( stream, resource );
}

 

方法3 

protected Configuration doConfigure(InputStream stream, String resourceName) throws HibernateException {
  try {
   List errors = new ArrayList();
   Document document = xmlHelper.createSAXReader( resourceName, errors, entityResolver )
     .read( new InputSource( stream ) );
   if ( errors.size() != 0 ) {
    throw new MappingException( "invalid configuration", (Throwable) errors.get( 0 ) );
   }
   doConfigure( document );
  }
  catch (DocumentException e) {
   throw new HibernateException( "Could not parse configuration: " + resourceName, e );
  }
  finally {
   try {
    stream.close();
   }
   catch (IOException ioe) {
    log.warn( "could not close input stream for: " + resourceName, ioe );
   }
  }
  return this;
 }

 

方法4

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;
}

 

以上的只是把配置文件转换成流,再通过dom4j包来处理(不了解dom4j的朋友,把它给开苞吧),到方法4中都接点了。

 

上面红色部分就是写入配置文件的关键方法。

 

先说说addProperties( sfNode )方法

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是一个继承了Hashtable的类生成的对象。之后这个标签就都放这了,以后慢慢用吧。

 

再看看parseSessionFactory( sfNode, name )方法

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 ) ) {
    String className = subelement.attributeValue( "class" );
    Attribute regionNode = subelement.attribute( "region" );
    final String region = ( regionNode == null ) ? className : regionNode.getValue();
    boolean includeLazy = !"non-lazy".equals( subelement.attributeValue( "include" ) );
    setCacheConcurrencyStrategy( className, subelement.attributeValue( "usage" ), region, includeLazy );
   }
   else if ( "collection-cache".equals( subelementName ) ) {
    String role = subelement.attributeValue( "collection" );
    Attribute regionNode = subelement.attribute( "region" );
    final String region = ( regionNode == null ) ? role : regionNode.getValue();
    setCollectionCacheConcurrencyStrategy( role, subelement.attributeValue( "usage" ), region );
   }
   else if ( "listener".equals( subelementName ) ) {
    parseListener( subelement );
   }
   else if ( "event".equals( subelementName ) ) {
    parseEvent( subelement );
   }
  }
 }

其实都是各自的方法set各自的东西,如果想看都怎么写的,那就一直点进去看看吧,比如第一个parseMappingElement( subelement, name );最后到下面的方法体中(3.6.0版本代码)

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中。

 

在这里,根据我看过一篇博客中(地址:点我),在parseMappingElement( subelement, name );最后调用的方法是(3.3.0版本代码)

public Configuration addInputStream(InputStream xmlInputStream) throws MappingException {
try {
List errors = new ArrayList();
org.dom4j.Document doc = xmlHelper.createSAXReader( "XML InputStream", errors, entityResolver )
.read( new InputSource( xmlInputStream ) );
if ( errors.size() != 0 ) {
throw new InvalidMappingException( "invalid mapping", null, (Throwable) errors.get( 0 ) );
}
add( doc );
return this;
}
catch (DocumentException e) {
throw new InvalidMappingException( "input stream", null, e );
}
finally {
try {
xmlInputStream.close();
}
catch (IOException ioe) {
log.warn( "Could not close input stream", ioe );
}
}
}

 

上面的代码是(http://anonsvn.jboss.org/repos/hibernate/core/tags/hibernate-3.3.0.CR1/core/src/main/java/org/hibernate/cfg/Configuration.java)

这个版本时的代码,看清版本号3.3.0

再调用下面的add方法

 

protected void add(org.dom4j.Document doc) throws MappingException {
HbmBinder.bindRoot( doc, createMappings(), CollectionHelper.EMPTY_MAP );
}

/**
* Create a new <tt>Mappings</tt> to add class and collection
* mappings to.
*/
public Mappings createMappings() {
return new Mappings(
classes,
collections,
tables,
namedQueries,
namedSqlQueries,
sqlResultSetMappings,
imports,
secondPasses,
propertyReferences,
namingStrategy,
typeDefs,
filterDefinitions,
extendsQueue,
auxiliaryDatabaseObjects,
tableNameBinding,
columnNameBindingPerTable
);
}

会主动的实例化一个Mappings类。

 

以上都是3.3.0时的代码,目的就是建立ConfigurationMappings是一对多的关系,用Mappings类作为桥梁实例化这些字段,而不添加一个Mappings类的引用

而最新的3.6.0版本中的代码,已经发生了变化。

 

之前Mappings是实体类,现在Mappings为接口,并在Configuration中有一个Mappings的实现类MappingsImpl,刚好是上面说的。

 

 

 

 

 

其实Configuration conf=new Configuration();的第一步,还会创建一个new SettingsFactory(),这个SettingsFactory类,就在下篇再细说吧。

 

总的来说,Configuration在new与config() 方法的调用,buildSessionFactory()的过程中。

 

1,加载xml文件,再解析,把property标签的内容放在properties中,properties是一个Map。

2,而把另一部分mapping标签的内容放到内容类MetadataSourceQueue的私有变量中。

3,调用buildSessionFactory()后,给相应的参数赋值,完成SessionFactory的创建。

 

如果想进一步的了解,对应xml第一个属性,都是都有一个方法来解析。

 

通过对这个类的观察,可以充分理解抽象工厂的使用,学习源代码,其实就是学习人家的设计模式,而且代码中加入了很多的方式防止因为一个配置不正确,导致的代码问题;并将错误写入日志,以供排查。

 

与3.3.0相比,最主要的是多了两个内部类:

MappingsImpl与MetadataSourceQueue。

 

当然其中一个方法,对后面的分析也是很深远的,就是setListeners(String type, Object[] listeners)这个方法中根据类型,增加事务处理监控,这个之后再深入吧。

 

看完这个,也许,你会发现其中有些东西搞错了,换理解有问题,那么,请您帮我指出,每一个错误的指出,都是我对hibernate理解的一次加深。

 

欢迎讨论。

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值