Learing NHibernate 1st

一. 准备工作

  1.下载最新版本的NHibernate, 本文使用的是NHibernate-3.3.3, 下载地址为http://nhforge.org/

  2.新建一个控制台应用程序, 命名为ConsoleApp

  3.引用添加NHibernate程序集, 该程序集位于解压文件夹的Required_Bins下面

二. 配置NHibernate

  1.在ConsoleApp程序中新建一个xml文件, 并命名为hibernate.cfg.xml(注意名称一定要一致)

  2.为xml文件新增架构schema, 添加nhibernate-configuration.xsd的引用, 该文件同样位于解压文件夹的Required_Bins目录下(使用架构, 可以启用xml智能提示功能, 为编写配置文件带来便利)

  3.在xml文件中写入配置信息, 本机使用的是SqlServer 2008, 写法如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">Server=home-pc\SQLEXPRESS;Database = Test;Integrated Security=True</property>
    <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
    <mapping assembly="ConsoleApp"/>
  </session-factory>
</hibernate-configuration>
配置信息

hibernate-configuration是根节点, xmlns是xml namespace的缩写, 作为一个标识作用,  避免相同名称的标签使用歧义. 

session-factory是表示会话工厂的节点.

"connection.driver_class"的property表示使用哪个数据库驱动类. 这里的"NHibernate.Driver.SqlClientDriver"其实就是一个真实存在的类, 用ILSpy查看NHibernate程序集, 可以看到SqlClientDriver封装了操作SqlServer的ADO.NET对象.

"connection.connection_string"property的作用是存储数据库连接字符串.

"dialect"中文翻译为方言的意思, 这是由于即使是SqlServer, 不同版本的SqlServer会存在一些差异, 主要包括数据类型和sql语句编写的不同. 同样这里的"NHibernate.Dialect.MsSql2008Dialect"也是一个类.

mapping是表示程序集的名称, NHibernate会搜索这个命名空间下为嵌入的资源且命名结尾为.hbm.xml的文件.

  4.把hibernate.cfg.xml文件的属性复制到输出目录设置为始终复制, 这样该文件就会自动发布到bin文件夹下.

三. 测试NHibernate是否配置成功

  1.在SqlServer中创建一个新的数据库, 命名为Test

  2.在ConsoleApp的Program.cs加上using NHibernate; 和 using  NHibernate.Cfg;

  3.在static void Main(string args[])中写上如下代码:

ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();

  4.运行程序, 如果程序没有出错的话, 恭喜, 你的NHibernate配置成功了!

四.  NHibernate创建SessionFactory的执行过程

  接下来根据NHibernate的源码, 来探讨一下创建SessionFactory的过程究竟执行了什么操作.

//Part 1 
public Configuration() : this(new SettingsFactory())
{
}

//Part 1.1
protected Configuration(SettingsFactory settingsFactory)
{
    this.InitBlock();
    this.settingsFactory = settingsFactory;
    this.Reset();
}

//Part 1.1.1
private void InitBlock()
{
    this.mapping = this.BuildMapping();
}

//Part 1.1.1.1
public virtual IMapping BuildMapping()
{
    return new Configuration.Mapping(this);
}

//Part 1.1.2
protected void Reset()
{
    this.classes = new Dictionary<string, PersistentClass>();
    this.Imports = new Dictionary<string, string>();
    this.collections = new Dictionary<string, Collection>();
    this.tables = new Dictionary<string, Table>();
    this.NamedQueries = new Dictionary<string, NamedQueryDefinition>();
    this.NamedSQLQueries = new Dictionary<string, NamedSQLQueryDefinition>();
    this.SqlResultSetMappings = new Dictionary<string, ResultSetMappingDefinition>();
    this.secondPasses = new List<SecondPassCommand>();
    this.propertyReferences = new List<Mappings.PropertyReference>();
    this.FilterDefinitions = new Dictionary<string, FilterDefinition>();
    this.interceptor = Configuration.emptyInterceptor;
    this.properties = Environment.Properties;
    this.auxiliaryDatabaseObjects = new List<IAuxiliaryDatabaseObject>();
    this.SqlFunctions = new Dictionary<string, ISQLFunction>();
    this.mappingsQueue = new MappingsQueue();
    this.eventListeners = new EventListeners();
    this.typeDefs = new Dictionary<string, TypeDef>();
    this.extendsQueue = new HashedSet<ExtendsQueueEntry>();
    this.tableNameBinding = new Dictionary<string, Mappings.TableDescription>();
    this.columnNameBindingPerTable = new Dictionary<Table, Mappings.ColumnNames>();
    this.filtersSecondPasses = new Queue<FilterSecondPassArgs>();
}
Part 1

  可以看到new Configuration() 一直在实例化一些对象, 并没有执行其他有关加载配置信息的操作. 

//Part 2
public Configuration Configure()
{
    IHibernateConfiguration hc = ConfigurationManager.GetSection("hibernate-configuration") as IHibernateConfiguration;
    if (hc != null && hc.SessionFactory != null)
    {
        return this.DoConfigure(hc.SessionFactory);
    }
    return this.Configure(this.GetDefaultConfigurationFilePath());
}
Part 2

  这里先从系统的配置文件中读取“hibernate-configuration”节点, 如果系统配置文件不存在这个节点的话, 再调用this.Configure(this.GetDefaultConfigurationFilePath())执行配置.

//Part 2.1
protected virtual string GetDefaultConfigurationFilePath()
{
    string baseDir = AppDomain.CurrentDomain.BaseDirectory;
    string searchPath = AppDomain.CurrentDomain.RelativeSearchPath ?? string.Empty;
    string relativeSearchPath = searchPath.Split(new char[]
    {
        ';'
    }).First<string>();
    string binPath = Path.Combine(baseDir, relativeSearchPath);
    return Path.Combine(binPath, "hibernate.cfg.xml");
}
Part 2.1
//Part 2.2
public Configuration Configure(string fileName)
{
    return this.Configure(fileName, false);
}

//Part 2.2.1
private Configuration Configure(string fileName, bool ignoreSessionFactoryConfig)
{
    if (ignoreSessionFactoryConfig)
    {
        Environment.ResetSessionFactoryProperties();
        this.properties = Environment.Properties;
    }
    XmlTextReader reader = null;
    Configuration result;
    try
    {
        reader = new XmlTextReader(fileName);
        result = this.Configure(reader);
    }
    finally
    {
        if (reader != null)
        {
            reader.Close();
        }
    }
    return result;
}

//Part 2.2.1.1
public Configuration Configure(XmlReader textReader)
{
    if (textReader == null)
    {
        throw new HibernateConfigException("Could not configure NHibernate.", new ArgumentException("A null value was passed in.", "textReader"));
    }
    Configuration result;
    try
    {
        IHibernateConfiguration hc = new HibernateConfiguration(textReader);
        result = this.DoConfigure(hc.SessionFactory);
    }
    catch (Exception e)
    {
        Configuration.log.Error("Problem parsing configuration", e);
        throw;
    }
    return result;
}
Part 2.2 - 2.2.1 - 2.2.1.1

 

//Part 2.2.1.1.1
public HibernateConfiguration(XmlReader hbConfigurationReader) : this(hbConfigurationReader, false)
{
}
private HibernateConfiguration(XmlReader hbConfigurationReader, bool fromAppSetting)
{
    XPathNavigator nav;
    try
    {
        nav = new XPathDocument(XmlReader.Create(hbConfigurationReader, this.GetSettings())).CreateNavigator();
    }
    catch (HibernateConfigException)
    {
        throw;
    }
    catch (Exception e)
    {
        throw new HibernateConfigException(e);
    }
    this.Parse(nav, fromAppSetting);
}

private void Parse(XPathNavigator navigator, bool fromAppConfig)
{
    this.ParseByteCodeProvider(navigator, fromAppConfig);
    this.ParseReflectionOptimizer(navigator, fromAppConfig);
    XPathNavigator xpn = navigator.SelectSingleNode(CfgXmlHelper.SessionFactoryExpression);
    if (xpn != null)
    {
        this.sessionFactory = new SessionFactoryConfiguration(navigator);
        return;
    }
    if (!fromAppConfig)
    {
        throw new HibernateConfigException("<session-factory xmlns='urn:nhibernate-configuration-2.2'> element was not found in the configuration file.");
    }
}

internal SessionFactoryConfiguration(XPathNavigator hbConfigurationSection)
{
    if (hbConfigurationSection == null)
    {
        throw new ArgumentNullException("hbConfigurationSection");
    }
    this.Parse(hbConfigurationSection);
}

private void Parse(XPathNavigator navigator)
{
    this.ParseName(navigator);
    this.ParseProperties(navigator);
    this.ParseMappings(navigator);
    this.ParseClassesCache(navigator);
    this.ParseCollectionsCache(navigator);
    this.ParseListeners(navigator);
    this.ParseEvents(navigator);
}

/*
*这里一直在读取配置文件中的session-factory下的节点所代表的信息
*property, mapping, class-cache, collection-cache, listener, event都可以在session-factory下配置
*/
Part 2.2.1.1.1

 

// NHibernate.Cfg.Configuration
protected Configuration DoConfigure(ISessionFactoryConfiguration factoryConfiguration)
{
    if (!string.IsNullOrEmpty(factoryConfiguration.Name))
    {
        this.properties["session_factory_name"] = factoryConfiguration.Name;
    }
    this.AddProperties(factoryConfiguration);
    foreach (MappingConfiguration mc in factoryConfiguration.Mappings)
    {
        if (mc.IsEmpty())
        {
            throw new HibernateConfigException("<mapping> element in configuration specifies no attributes");
        }
        if (!string.IsNullOrEmpty(mc.Resource) && !string.IsNullOrEmpty(mc.Assembly))
        {
            Configuration.log.Debug(string.Concat(new string[]
            {
                factoryConfiguration.Name,
                "<-",
                mc.Resource,
                " in ",
                mc.Assembly
            }));
            this.AddResource(mc.Resource, Assembly.Load(mc.Assembly));
        }
        else
        {
            if (!string.IsNullOrEmpty(mc.Assembly))
            {
                Configuration.log.Debug(factoryConfiguration.Name + "<-" + mc.Assembly);
                this.AddAssembly(mc.Assembly);
            }
            else
            {
                if (!string.IsNullOrEmpty(mc.File))
                {
                    Configuration.log.Debug(factoryConfiguration.Name + "<-" + mc.File);
                    this.AddFile(mc.File);
                }
            }
        }
    }
    foreach (ClassCacheConfiguration ccc in factoryConfiguration.ClassesCache)
    {
        string region = string.IsNullOrEmpty(ccc.Region) ? ccc.Class : ccc.Region;
        bool includeLazy = ccc.Include != ClassCacheInclude.NonLazy;
        this.SetCacheConcurrencyStrategy(ccc.Class, EntityCacheUsageParser.ToString(ccc.Usage), region, includeLazy);
    }
    foreach (CollectionCacheConfiguration ccc2 in factoryConfiguration.CollectionsCache)
    {
        string role = ccc2.Collection;
        if (this.GetCollectionMapping(role) == null)
        {
            throw new HibernateConfigException("collection-cache Configuration: Cannot configure cache for unknown collection role " + role);
        }
        string region2 = string.IsNullOrEmpty(ccc2.Region) ? role : ccc2.Region;
        this.SetCollectionCacheConcurrencyStrategy(role, EntityCacheUsageParser.ToString(ccc2.Usage), region2);
    }
    foreach (EventConfiguration ec in factoryConfiguration.Events)
    {
        string[] listenerClasses = new string[ec.Listeners.Count];
        for (int i = 0; i < ec.Listeners.Count; i++)
        {
            listenerClasses[i] = ec.Listeners[i].Class;
        }
        Configuration.log.Debug(string.Concat(new object[]
        {
            "Event listeners: ",
            ec.Type,
            "=",
            StringHelper.ToString(listenerClasses)
        }));
        this.SetListeners(ec.Type, listenerClasses);
    }
    foreach (ListenerConfiguration lc in factoryConfiguration.Listeners)
    {
        Configuration.log.Debug(string.Concat(new object[]
        {
            "Event listener: ",
            lc.Type,
            "=",
            lc.Class
        }));
        this.SetListeners(lc.Type, new string[]
        {
            lc.Class
        });
    }
    if (!string.IsNullOrEmpty(factoryConfiguration.Name))
    {
        Configuration.log.Info("Configured SessionFactory: " + factoryConfiguration.Name);
    }
    Configuration.log.Debug("properties: " + this.properties);
    return this;
}
Part 2.2.1.1.2
  这里的DoConfigure() 方法主要是执行加载hbm.xml文件的配置信息, 同时反射实体类的信息.
//Part 2.3
public ISessionFactory BuildSessionFactory()
{
    this.ConfigureProxyFactoryFactory();
    this.SecondPassCompile();
    this.Validate();
    Environment.VerifyProperties(this.properties);
    Settings settings = this.BuildSettings();
    this.Schemas = null;
    return new SessionFactoryImpl(this, this.mapping, settings, this.GetInitializedEventListeners());
}
Part 2.3

   总结: 整个执行过程主要包括加载配置文件的信息, 加载对象的hbm.xml匹配信息, 反射对应的实体类的信息, 所以创建SessionFactory的过程开销很大, 应用程序启动的时候应该只执行一次创建SessionFactory的操作.

posted @ 2013-11-28 12:04  teroy  阅读(341)  评论(0)    收藏  举报