1. 程式人生 > 實用技巧 >NHibernate之(20):再探SchemaExport工具使用

NHibernate之(20):再探SchemaExport工具使用

本節內容

引入

上篇我們初步探索了SchemaExport工具使用,知道如何使用SchemaExport工具和SchemaUpdate工具利用NHibernate持久化類和對映檔案刪除、建立、更新資料庫架構,這篇具體分析如何為表字段增加一些約束?如何生成儲存過程?如何生成檢視?使用SchemaExport工具幫你搞定。

例項分析

1.表及其約束

眾所周知,SchemaExport工具根據對映檔案來生成資料庫架構,在對映檔案中通過Class對映可以很方便的生成資料庫表。但是這篇我們看看對映的條件,所以我重新定義兩個實體CategorySchema和ProductSchema,一對多關係。

Step1:兩個實體持久化類編寫程式碼如下:

public class CategorySchema
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}
public class ProductSchema
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual int UnitsOnStock { get; set
; } public virtual CategorySchema CategorySchema { get; set; } }

Step2:為兩個實體對映,使用最簡方式,編寫程式碼如下:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="DomainModel" namespace="DomainModel">

  <class name="DomainModel.Entities.CategorySchema,DomainModel
"> <id name="Id"> <generator class="guid"/> </id> <property name="Name"/> </class> <class name="DomainModel.Entities.ProductSchema,DomainModel"> <id name="Id"> <generator class="guid"/> </id> <property name="Name"/> <many-to-one name="CategorySchema" class="DomainModel.Entities.CategorySchema,DomainModel"/> </class> </hibernate-mapping>

Step3:編寫測試用例用於生成資料庫架構:

[Test]
public void ExecuteSchemaTest()
{
    var export = new SchemaExport(_cfg);
    export.Execute(true, true, false, true);
}

Step4:測試!NHibernate生成語句如下:

if exists (select 1 from sys.objects 
           where object_id = OBJECT_ID(N'[FK45BBFB51BC9515A6]')
           AND parent_object_id = OBJECT_ID('ProductSchema'))
alter table ProductSchema  drop constraint FK45BBFB51BC9515A6

if exists (select * from dbo.sysobjects 
           where id = object_id(N'ProductSchema')
           and OBJECTPROPERTY(id, N'IsUserTable') = 1)
   drop table ProductSchema
if exists (select * from dbo.sysobjects
           where id = object_id(N'CategorySchema')
           and OBJECTPROPERTY(id, N'IsUserTable') = 1)
   drop table CategorySchema

create table ProductSchema (
   Id UNIQUEIDENTIFIER not null,
   Name NVARCHAR(255) null,
   CategorySchema UNIQUEIDENTIFIER null,
   primary key (Id)
)
create table CategorySchema (
   Id UNIQUEIDENTIFIER not null,
   Name NVARCHAR(255) null,
   primary key (Id)
)

alter table ProductSchema add constraint FK45BBFB51BC9515A6
foreign key (CategorySchema) references CategorySchema

仔細看看生成的語句,都按預設的值生成了表,Name列字串型別NVARCHAR(255),預設為null;外來鍵預設一數字字串等。

1.設定非空型別和長度

在對映檔案中為ProductSchema實體的Name屬性新增:not-null="true"表示非空型別,length="50":列長度設定為50,程式碼片段如下:

<property name="Name" not-null="true" length="50"/>

測試,生成語句如下:

create table ProductSchema (
   Id UNIQUEIDENTIFIER not null,
   Name NVARCHAR(50) not null,
   CategorySchema UNIQUEIDENTIFIER null,
   primary key (Id)
)

2.設定外來鍵Foreign Keys

在對映檔案設定外來鍵名稱,注意有的需要兩邊都要設定才生效,程式碼片段如下:

<many-to-one name="CategorySchema"
             class="DomainModel.Entities.CategorySchema,DomainModel"
             foreign-key="FK_Product_Category"/>

生成語句如下:

if exists (select 1 from sys.objects
           where object_id = OBJECT_ID(N'[FK_Product_Category]')
           AND parent_object_id = OBJECT_ID('ProductSchema'))
alter table ProductSchema  drop constraint FK_Product_Category
...
alter table ProductSchema add constraint FK_Product_Category
foreign key (CategorySchema) references CategorySchema

3.設定Unique約束

我們要求Name欄位唯一,新增Unique約束,程式碼片段如下:

<property name="Name" not-null="true" length="50" unique="true"/>

生成語句如下:

create table ProductSchema (
   Id UNIQUEIDENTIFIER not null,
   Name NVARCHAR(50) not null unique,
   CategorySchema UNIQUEIDENTIFIER null,
   primary key (Id)
)

還有一種unique-key約束,同時為兩個屬性設定unique-key約束。我們為Customer持久化類的FirstName和LastName屬性新增Unique約束:

編寫對映,設定FirstName和LastName的Unique約束為UK_Person_Name:

<property name="FirstName" not-null="true"
          length="50" unique-key="UK_Customer_Name"/>
<property name="LastName" not-null="true"
          length="50" unique-key="UK_Customer_Name"/>

生成語句如下:

create table Customer(
   CustomerId INT IDENTITY not null,
   FirstName NVARCHAR(50) not null,
   LastName NVARCHAR(50) not null,
   primary key (CustomerId),
   unique (FirstName, LastName)
)

4.設定索引Index

<property name="Name" not-null="true" length="50" 
          unique="true" index="IDX_Product_Name" />

生成語句如下:

create table ProductSchema (Id UNIQUEIDENTIFIER...)
create table CategorySchema (Id UNIQUEIDENTIFIER...)
create index IDX_Product_Name on ProductSchema (Name)

5.設定Check約束

我們為UnitsOnStock值設定大於等於0:

<property name="UnitsOnStock" not-null="true" >
  <column name="UnitsOnStock" check="UnitsOnStock >= 0"/>
</property>

生成語句如下:

create table ProductSchema (
   Id UNIQUEIDENTIFIER not null,
   Name NVARCHAR(50) not null unique,
   UnitsOnStock INT null check( UnitsOnStock >= 0) ,
   CategorySchema UNIQUEIDENTIFIER null,
   primary key (Id)
)

好了,還有很多設定大家自己探索啦!知道了這些我們在寫對映的時候就注意啦!我之前對映檔案屬性對映為什麼寫的比較全呢?就是這個原因,便於生成資料庫架構是自己約束的並不是預設的。

2.儲存過程、檢視

除了表,我們還有儲存過程和檢視。怎麼利用SchemaExport工具生成儲存過程和檢視呢?在對映檔案中提供了database-object元素用來建立和刪除資料庫物件。

<database-object>
  <create>建立儲存過程或檢視語句等資料庫物件</create>
  <drop>刪除儲存過程或檢視語句等資料庫物件</drop>
</database-object>

1.儲存過程

還記得我們在NHibernate之旅(17):探索NHibernate中使用儲存過程(下)中建立的三個儲存過程了嗎?當時我們在資料庫中手寫建立的啊。現在完全可以不用那種古老的方法啦。我們在對映檔案中編寫database-object:在建立資料庫架構時建立名為entitySProcs的儲存過程,在刪除資料庫架構時刪除名為entitySProcs的儲存過程。現在使用SchemaExport工具不僅僅生成了表,還生成了儲存過程。還剩兩個儲存過程就留給大家去完成吧。

<database-object>
   <create>
     CREATE PROCEDURE entitySProcs
     AS
     SELECT CustomerId,Version,Firstname,Lastname FROM Customer
   </create>
   <drop>
     DROP PROCEDURE entitySProcs
   </drop>
 </database-object>

2.檢視

還記得我們在NHibernate之旅(14):探索NHibernate中使用檢視中建立了第一個檢視了嗎?那時我們也是在資料庫中手動建立的,現在我們可以自動建立了!開啟CustomerView.hbm.xml檔案,新增database-object:在建立資料庫架構時建立名為viewCustomer的檢視,在刪除資料庫架構時刪除名為viewCustomer的檢視。

<database-object>
  <create>
    CREATE VIEW [dbo].[viewCustomer]
    AS
    SELECT DISTINCT c.CustomerId, c.Firstname, c.Lastname, o.OrderId, o.OrderDate
    FROM         dbo.Customer AS c INNER JOIN
    dbo.[Order] AS o ON c.CustomerId = o.OrderId INNER JOIN
    dbo.OrderProduct AS op ON o.OrderId = op.[Order] INNER JOIN
    dbo.Product AS p ON op.Product = p.ProductId
    GROUP BY c.CustomerId, c.Firstname, c.Lastname, o.OrderId, o.OrderDate
  </create>
  <drop>drop view dbo.viewCustomer</drop>
</database-object>

測試生成資料庫架構,出現錯誤“資料庫中已存在名為'viewCustomer'的物件”。這是什麼原因呢?看看NHibernate生成語句:

create table viewCustomer (
   CustomerId INT IDENTITY NOT NULL,
   Firstname NVARCHAR(255) null,
   Lastname NVARCHAR(255) null,
   OrderId INT null,
   OrderDate DATETIME null,
   primary key (CustomerId)
)

觀察NHibernate生成SQL語句發現NHibernate利用Class對映自動生成了viewCustomer表,因為NHibernate見到Class對映就認為是表,它不知道這裡對映的是檢視,檢視和表在對映檔案中沒有什麼區別。我們修改一下這個對映檔案,在database-object元素上面再新增一個database-object用於刪除NHibernate生成的表。

<database-object>
  <create>drop table dbo.viewCustomer</create>
  <drop>drop table dbo.viewCustomer</drop>
</database-object>

這個database-object的意思就是在建立資料庫架構時刪除NHibernate自動生成的表viewCustomer,在刪除資料庫架構時刪除表viewCustomer。為什麼刪除兩次呢?因為我們有可能只Drop,那麼NHibernate就執行database-object中的Drop。

這樣在CustomerView.hbm.xml檔案中就有兩個database-object了,總體的意思就是在建立資料庫架構時刪除NHibernate自動生成的表viewCustomer並建立名為viewCustomer的檢視,在刪除資料庫架構時刪除名為viewCustomer表和名為viewCustomer的檢視。

結語

好了,終於把SchemaExport工具研究透徹了,應該沒有說漏別的東西,就到這裡了。下面看看NHibernate物件去,什麼狀態啦,快取啦。

本系列連結:NHibernate之旅系列文章導航

NHibernate Q&A

下次繼續分享NHibernate!