• Spring.NET教程(十六)——整合NHibernate(基础篇)


    NHibernate是一个基于.Net的针对关系型数据库的对象持久化(ORM)类库。NHibernate来源于非常优秀的基于Java的NHibernate关系型持久化工具。从数据库底NHibernate来持久化你的.Net 对象到关系型数据库。NHibernate为你处理这些,远胜于你不得不写SQL去从数据库存取对象。你的代码仅仅和对象关联,NHibernate自动产生SQL语句,并确保对象提交到正确的表和字段中去。

      Spring.NET对NHibernate提供了很好的支持与封装。Spring.Data.NHibernate.Generic.Support和Spring.Data.NHibernate.Support下的HibernateDaoSupport是Spring.NET提供的数据库访问对象(DAO)的基类,两者的却别在于对泛型的支持程度。我们以Spring.Data.NHibernate.Generic.Support.HibernateDaoSupport为例,讲解Spring.NET整合NHibernate开发。

      我归纳了一下,分为三个步骤:

      一、实体对象的建立及配置

      二、数据访问对象建立及配置

      三、业务处理层建立及配置

      首先让我们学习一下NHibernate的实体对象的映射:我建立两个实体“用户信息”和“公司信息”。图1所示。

    Spring.NET教程(十八)——整合NHibernate(基础篇) Level 300

      图1


    Model

        public class User
        {
            public virtual int? UserID { get; set; }
            public virtual string UserName { get; set; }
            public virtual int UserAge { get; set; }
            public virtual bool UserSex { get; set; }
            public virtual Company CurrentCompany { get; set; }
        }
        public class Company
        {
            public virtual int? CompanyID { get; set; }
            public virtual string CompanyName { get; set; }
            public virtual IList<User> UserList { get; set; }
        }


      NHibernate要求实体必须是带有无参构造函数和带有virtual修饰的属性。两个实体的关系是双向(一对多——多对一)映射关系。


    xxx.hbm.xml

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
      <class name="Model.User, Model" table="UserInfo">
        
        <id name="UserID" column="UserID" type="int" >
          <generator class="native" />
        </id>
        <property name="UserName" column="UserName" type="string" length="50" not-null="true"/>
        <property name="UserAge" column="UserAge" type="int"/>
        <property name="UserSex" column="UserSex" type="bool"/>
        <many-to-one name="CurrentCompany" class="Model.Company, Model" foreign-key="FK_UserInfo_CompanyInfo">
          <column name="CompanyID" not-null="true" />
        </many-to-one>
      </class>
    </hibernate-mapping>

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
      <class name="Model.Company, Model" table="CompanyInfo">
        
        <id name="CompanyID" column="CompanyID" type="int" >
          <generator class="native" />
        </id>
        <property name="CompanyName" column="Name" type="string" not-null="true" length="50"/>
        <bag name="UserList" inverse="true" cascade="all-delete-orphan" table="UserInfo">
          <key column="CompanyID" foreign-key="FK_UserInfo_CompanyInfo"/>
          <one-to-many class="Model.User, Model" />
        </bag>
      </class>
    </hibernate-mapping>

      以上就是实体对象与数据的映射文件,提供的配置我不详细说明,请查看NHibernate的帮助手册。

      接下来,我建立数据库访问对象(DAO)层。在这里我使用了泛型Repository模式。


    Repository

        public interface IRepository<T>
        {
            void Delete(T entity);
            T Get(object id);
            object Save(T entity);
            void Update(T entity);
        }
       public class NHibernateRepository<T> : HibernateDaoSupport, IRepository<T>
        {
            public object Save(T entity)
            {
                return this.HibernateTemplate.Save(entity);
            }
            public T Get(object id)
            {
                return this.HibernateTemplate.Get<T>(id);
            }
            public void Update(T entity)
            {
                this.HibernateTemplate.Update(entity);
            }
            public void Delete(T entity)
            {
                this.HibernateTemplate.Delete(entity);
            }
        }

      数据库访问对象我们可以让它继承于HibernateDaoSupport类,该类的HibernateTemplate属性我们可以通过Spring.NET从外部注入。


    Repository.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="http://www.springframework.net"
             xmlns:db="http://www.springframework.net/database">
      <!-- 用以我们在其它的应用程序中,配置数据访问 -->
      <object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
        <property name="ConfigSections" value="databaseSettings"/>
      </object>
      <!-- 数据库和Nhibernate的相关配置 -->
      <db:provider id="DbProvider" provider="SqlServer-1.1" 
                   connectionString="Server=${db.datasource};database=${db.database};uid=${db.user};pwd=${db.password};"/>
      <!--SessionFactory对象,其中包括一些比较重要的属性 -->
      <object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="MappingAssemblies">
          <list>
            <value>Model</value>
          </list>
        </property>
        <property name="HibernateProperties">
          <dictionary>
            <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
            <entry key="dialect" value="NHibernate.Dialect.MsSql2000Dialect"/>
            <entry key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>
            <entry key="use_outer_join" value="true"/>
            <entry key="show_sql" value="false"/>
            <!--自动建表(反向映射)-->
            <entry key="hbm2ddl.auto" value="update"/>
            <entry key="adonet.batch_size" value="10"/>
            <entry key="command_timeout" value="60"/>
            <!--显式启用二级缓存-->
            <entry key="cache.use_second_level_cache" value="true"/>
            <!--启动查询缓存-->
            <entry key="cache.use_query_cache" value="false"/>
            <entry key="query.substitutions" value="true 1, false 0, yes 'Y', no 'N"/>
            <entry key="proxyfactory.factory_class" value="NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle"/>
          </dictionary>
        </property>
        <property name="ExposeTransactionAwareSessionFactory" value="true" />
      </object>
      <object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
        <property name="SessionFactory" ref="NHibernateSessionFactory" />
        <property name="TemplateFlushMode" value="Auto" />
        <property name="CacheQueries" value="true" />
      </object>
      <object id="repository.user" type="Repository.NHibernateRepository&lt;Model.User>, Repository">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
      </object>
      <object id="repository.company" type="Repository.NHibernateRepository&lt;Model.Company>, Repository">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
      </object>

    </objects>

      db:provider节点是数据的连接字符串配置,我们引入xmlns:db="http://www.springframework.net/database这项命名空间便可以使用它。其中provider属性为数据库提供者的名称。以下是provider的详细情况:

      名称   介绍
      SqlServer-1.1   Microsoft SQL Server, provider V1.0.5.0 in framework .NET V1.1
      SqlServer-2.0   Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0
      SqlServerCe-3.1   Microsoft SQL Server Compact Edition, provider V9.0.242.0
      SqlServerCe-3.5.1   Microsoft SQL Server Compact Edition, provider V3.5.1.0
      OleDb-1.1   provider V1.0.5000.0 in framework .NET V1.1
      OleDb-2.0   provider V2.0.0.0 in framework .NET V2.0
      OracleClient-2.0   Oracle, Microsoft provider V2.0.0.0
      OracleODP-2.0   Oracle, Oracle provider V2.102.2.20
      MySql   MySQL provider 1.0.10.1
      MySql-1.0.9   MySQL provider 1.0.9
      MySql-5.0   MySQL provider 5.0.7.0
      MySql-5.0.8.1   MySQL provider 5.0.8.1
      MySql-5.1   MySQL provider 5.1.2.2
      MySql-5.1.4   MySQL provider 5.1.2.2
      MySql-5.2.3   MySQL provider 5.2.3.0
      Npgsql-1.0   Postgresql provider 1.0.0.0 (and 1.0.0.1 - were build with same version info)
      Npgsql-2.0-beta1   Postgresql provider 1.98.1.0 beta 1
      Npgsql-2.0   Postgresql provider 2.0.0.0
      DB2-9.0.0-1.1   IBM DB2 Data Provider 9.0.0 for .NET Framework 1.1
      DB2-9.0.0-2.0   IBM DB2 Data Provider 9.0.0 for .NET Framework 2.0
      DB2-9.1.0-1.1   IBM DB2 Data Provider 9.1.0 for .NET Framework 1.1
      DB2-9.1.0.2   IBM DB2 Data Provider 9.1.0 for .NET Framework 2.
      SQLite-1.0.43   SQLite provider 1.0.43 for .NET Framework 2.0
      SQLite-1.0.47   SQLite provider 1.0.43 for .NET Framework 2.0
      SybaseAse-12   Sybase ASE provider for ASE 12.x
      SybaseAse-15   Sybase ASE provider for ASE 15.x
      SybaseAse-AdoNet2   Sybase ADO.NET 2.0 provider for ASE 12.x and 15.x
      Odbc-1.1   ODBC provider V1.0.5000.0 in framework .NET V1.1
      Odbc-2.0   ODBC provider V2.0.0.0 in framework .NET V2
      InterSystems.Data.CacheClient   Caché provider Version 2.0.0.1 in framework .NET V2

      可以根据自己的数据库选择不同的提供者名称。connectionString属性为数据库的连接字符串,这里用${xxx}的方式来表示一个占位符,因为我们经常将Spring.NET的配置文件设置为“嵌入系统资源”,这样一来在程序编译后就不能够修改,所以我们就要在应用程序配置文件中填写连接字符串,而不是在Spring.NET的配置文件中填写。

      App.config

    <configuration>
    <configSections>
    <section name="databaseSettings" type="System.Configuration.NameValueSectionHandler"/>
    </configSections>
      <!--数据库连接字符串-->
      <databaseSettings>
        <add key="db.datasource" value="." />
        <add key="db.user" value="sa" />
        <add key="db.password" value="" />
        <add key="db.database" value="SpringNet_Lesson18" />
      </databaseSettings>
    </configuration>

      NHibernate中的Session控制取决于SessionFactory,Spring.NET提供了LocalSessionFactoryObject类来统一管理SessionFactory。其中MappingAssemblies属性为实体程序集的名称,可以填写多个名称。HibernateProperties为NHibernate的配置,dialect属性为数据库的方言,因为是SQL server 2K数据库,所以使用NHibernate.Dialect.MsSql2000Dialect 。proxyfactory.factory_class属性为延迟加载的代理类驱动,在NHibernate 2.1版中必须配置。hbm2ddl.auto属性为反向建立映射表的配置,我们配置为update后,NHibernate会帮我们自动根据实体的结构生成数据库中的表。

      接下来我们看一下业务处理层。


    UserManager

        public interface IUserManager
        {
            void Delete(User entity);
            User Get(object id);
            object Save(User entity);
            void Update(User entity);
            IRepository<User> UserRepository { get; set; }
        }
        public class UserManager : IUserManager
        {
            public IRepository<User> UserRepository { get; set; }
            public object Save(User entity)
            {
                return this.UserRepository.Save(entity);
            }
            public void Delete(User entity)
            {
                this.UserRepository.Delete(entity);
            }
            public User Get(object id)
            {
                return this.UserRepository.Get(id);
            }
            public void Update(User entity)
            {
                this.UserRepository.Update(entity);
            }
        }


    CompanyManager

        public interface ICompanyManager
        {
            void Delete(object id);
            Company Get(object id);
            object Save(Company entity);
            void Update(Company entity);
        }
        public class CompanyManager : ICompanyManager
        {
            public IRepository<Company> CompanyRepository { get; set; }
            public object Save(Company entity)
            {
                return this.CompanyRepository.Save(entity);
            }
            public void Delete(object id)
            {
                this.CompanyRepository.Delete(this.Get(id));
            }
            public Company Get(object id)
            {
                return this.CompanyRepository.Get(id);
            }
            public void Update(Company entity)
            {
                Company company = this.Get(entity.CompanyID);
                company.CompanyName = entity.CompanyName;
                this.CompanyRepository.Update(company);
            }
        }

      代码的编写我不仔细讲,我们主要学习一下相关的配置。


    Manager.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="http://www.springframework.net">
      <object id="transactionManager"
            type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate21">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
      </object>

      <object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data">
        <property name="TransactionManager" ref="transactionManager"/>
        <property name="TransactionAttributeSource">
          <object type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data"/>
        </property>
      </object>
      <object id="BaseTransactionManager"  type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data" abstract="true">
        <property name="PlatformTransactionManager" ref="transactionManager"/>
        <property name="TransactionAttributes">
          <name-values>
            <add key="Save*" value="PROPAGATION_REQUIRED"/>
            <add key="Set*" value="PROPAGATION_REQUIRED"/>
            <add key="Finish*" value="PROPAGATION_REQUIRED"/>
            <add key="Update*" value="PROPAGATION_REQUIRED"/>
            <add key="Delete*" value="PROPAGATION_REQUIRED"/>
            <add key="Add*" value="PROPAGATION_REQUIRED"/>
            <add key="Get*" value="PROPAGATION_SUPPORTS,readOnly"/>
            <add key="Find*" value="PROPAGATION_SUPPORTS,readOnly"/>
            <add key="Load*" value="PROPAGATION_SUPPORTS,readOnly"/>
            <add key="*" value="PROPAGATION_REQUIRED"/>
          </name-values>
        </property>
      </object>
      <object id="CompanyManager" parent="BaseTransactionManager">
        <property name="Target">
          <object type="Manager.CompanyManager,Manager">
            <property name="CompanyRepository" ref="repository.company"/>
          </object>
        </property>
      </object>
      <object id="UserManager" parent="BaseTransactionManager">
        <property name="Target">
          <object type="Manager.UserManager,Manager">
            <property name="UserRepository" ref="repository.user"/>
          </object>
        </property>
      </object>
    </objects>

      我们在前几篇学过AOP拦截和事务代理。Spring.NET为NHibernate提供的事务代理是TransactionProxyFactoryObject。我们将改类的Target熟悉注入业务处理层的类,这样Spring.NET会为该类包装上事务。

      最后我们写一个单元测试类,对业务层进行单元测试。
    UserManagerTest

    [TestFixture]
        public class UserManagerTest 
        {
            static log4net.ILog logger = log4net.LogManager.GetLogger("Logger");
            [SetUp]
            public void SetUp()
            {
                try
                {
                    log4net.Config.XmlConfigurator.Configure();
                    IApplicationContext applicationContext = ContextRegistry.GetContext();
                    userManager = (IUserManager)applicationContext.GetObject("UserManager");
                    companyManager = (ICompanyManager)applicationContext.GetObject("CompanyManager");
                }
                catch (Exception ex)
                {
                    logger.Error(ex);
                    throw ex;
                }
            }
            private IUserManager userManager;
            private ICompanyManager companyManager;
            [Test]
            public void Delete()
            {
                userManager.Delete(userManager.Get(2));
            }
            [Test]
            public void Get()
            {
                User user = userManager.Get(1);
            }
            [Test]
            public void Save()
            {
                User user = new User();
                user.UserName = "刘冬";
                user.CurrentCompany = companyManager.Get(1);
                userManager.Save(user);
            }
            [Test]
            public void Update()
            {
                User user = userManager.Get(1);
                user.UserName = "刘冬冬";
                userManager.Update(user);
            }
        }


    CompanyManagerTest

    [TestFixture]
        public class CompanyManagerTest 
        {
            static log4net.ILog logger = log4net.LogManager.GetLogger("Logger");
            [SetUp]
            public void SetUp()
            {
                try
                {
                    log4net.Config.XmlConfigurator.Configure();
                    IApplicationContext applicationContext = ContextRegistry.GetContext();
                    companyManager = (ICompanyManager)applicationContext.GetObject("CompanyManager");
                }
                catch (Exception ex)
                {
                    logger.Error(ex);
                    throw ex;
                }
            }
            private ICompanyManager companyManager;
            [Test]
            public void Delete()
            {
                companyManager.Delete(4);
            }
            [Test]
            public void Get()
            {
                Company company = companyManager.Get(1);
            }
            [Test]
            public void Save()
            {
                Company company = new Company();
                company.CompanyName = "刘冬公司";
                companyManager.Save(company);
            }
            [Test]
            public void Update()
            {
                Company company = companyManager.Get(1);
                company.CompanyName = "刘冬冬公司";
                companyManager.Update(company);
            }
        }

      配置文件:
    App.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      
      <configSections>
        <sectionGroup name="spring">
          <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
          <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
          <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
        </sectionGroup>
        
        <section name="databaseSettings" type="System.Configuration.NameValueSectionHandler"/>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
      </configSections>
      <spring>
        <parsers>
          <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data"/>
          <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data"/>
        </parsers>
        <context>
          <resource uri="assembly://Repository/Repository/Repository.xml"/>
          <resource uri="assembly://Manager/Manager/Manager.xml"/>
        </context>
      </spring>
      <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%-5level %logger - %message%newline" />
          </layout>
        </appender>
        <!-- Set default logging level to DEBUG -->
        <root>
          <level value="DEBUG" />
          <appender-ref ref="ConsoleAppender" />
        </root>
        <!-- Set logging for Spring.  Logger names in Spring correspond to the namespace -->
        <logger name="Spring">
          <level value="INFO" />
        </logger>
        <logger name="Spring.Data">
          <level value="DEBUG" />
        </logger>
        <logger name="NHibernate">
          <level value="INFO" />
        </logger>

      </log4net>
      <!--数据库连接字符串-->
      <databaseSettings>
        <add key="db.datasource" value="." />
        <add key="db.user" value="sa" />
        <add key="db.password" value="" />
        <add key="db.database" value="SpringNet_Lesson18" />
      </databaseSettings>
    </configuration>

  • 相关阅读:
    Swift
    UIWindow 详解及使用场景
    点击状态栏回到顶部的功能失效的解决办法
    iOS
    从经典问题来看 Copy 方法
    从汇编层面深度剖析C++虚函数
    数值的整数次方
    求整数二进制中1的个数
    C++中的位运算总结
    嵌入在C++程序中的extern "C"
  • 原文地址:https://www.cnblogs.com/millen/p/1635980.html
Copyright © 2020-2023  润新知