上一篇: 下一篇:
一切从配置开始吧!
对于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 );
}
}
}
这个版本时的代码,看清版本号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时的代码,目的就是建立Configuration和Mappings是一对多的关系,用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理解的一次加深。
欢迎讨论。
本文详细剖析了Hibernate配置过程,包括配置文件的读取、解析及如何将配置信息映射到内部数据结构。通过源码分析,介绍了Configuration类的核心作用及其与SessionFactory的创建流程。
8713

被折叠的 条评论
为什么被折叠?



