1. 程式人生 > >Spring.NET教程(十八)整合NHibernate(基礎篇)

Spring.NET教程(十八)整合NHibernate(基礎篇)

uil com red port log4net odbc con 其它 get

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.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>

Spring.NET教程(十八)整合NHibernate(基礎篇)