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所示。
图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." xmlns:db="http://www./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<Model.User>, Repository"> <property name="HibernateTemplate" ref="HibernateTemplate"/> </object> <object id="repository.company" type="Repository.NHibernateRepository<Model.Company>, Repository"> <property name="HibernateTemplate" ref="HibernateTemplate"/> </object>
</objects>
db:provider节点是数据的连接字符串配置,我们引入xmlns:db="http://www./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."> <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>
|