这篇讲讲NHibernate的视图和存储过程的使用方法。
视图在Nhibernate中使用比较简单,这里大概讲解下:
首先创建一个视图查询CustomerId、Firstname、Lastname、OrderId、OrderDate字段。命名viewCustomer
viewCustomer可以看做成一张实体表,所以接下来就和普通表一样在Nhibernate中设置就行了。
实体类


{
public virtual int CustomerId { get; private set; }
public virtual string Firstname { get; private set; }
public virtual string Lastname { get; private set; }
public virtual int OrderId { get; private set; }
public virtual DateTime OrderDate { get; private set; }
}
映射文件CustomerView.hbm.xml


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernateSample.Domain" namespace="NHibernateSample.Domain.Entities">
<class name="NHibernateSample.Domain.Entities.CustomerView,NHibernateSample.Domain.Entities"
table="viewCustomer" mutable="false" >
<id name="CustomerId" column="CustomerId" type="Int32">
<generator class="native" />
</id>
<property name="Firstname" column="Firstname" type="string" />
<property name="Lastname" column="Lastname" type="string" />
<property name="OrderId" column="OrderId" type="Int32" />
<property name="OrderDate" column="OrderDate" type="DateTime" />
</class>
</hibernate-mapping>
设置完后和普通的表使用方法一致:


{
return _session.CreateCriteria(typeof(CustomerView))
.Add(Restrictions.Gt("OrderDate", orderDate))
.List<CustomerView>();
}
这样就完成了视图的配置,很简单。
接下来是存储过程,这个比较麻烦,并且不符合OO。
首先我们来创建个简单的:


(
@CustomerId int,
@Version int
)
AS
DELETE
FROM [Customer]
WHERE
[CustomerId] = @CustomerId and [Version] = @Version
RETURN @@Error
注意这里的@Version,因为客户表里面有版本控制,不管你增删改,Nhibernate都会为你加上@Version的,所以单单写个CustomerID的话,就会多一个参数,因此这里加上了@Version参数,如果不是很明白的话看下图:
@P0为ID,那@P1呢,就是version。
存储过程创建完了,要怎么用呢,很简单,我们之前创建的业务逻辑不用做任何修改,只用在对应的映射文件进行修改就行了,这里是Custom所以在customer.hbm.XML中修改。
加上<sql-delete>exec CustomerDelete ?,?</sql-delete>
这个大家应该看的懂,<sql-delete>代表删除,exec CustomerDelete执行的存储过程,?代表参数,有几个参数就写几个。
不过注意一点,如果还有其他的增,删的话,要按一定顺序写,如下,不然会报错。


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel">
<class name ="DomainModel.Entities.Customer,DomainModel" table="Customer">
<!--存储过程-->
<sql-insert>exec CustomerInsert ?,?,?,?</sql-insert>
<sql-update>exec CustomerUpdate ?,?,?,?,?</sql-update>
<sql-delete>exec CustomerDelete ?,?</sql-delete>
</class>
</hibernate-mapping>
这样,就可以使用存储过程了,也就是说你只要在映射文件中加上<sql-delete>,删除的时候就会调用这里面的方法,但是要注意存储过程的参数要和映射文件的参数一致。
我们进行删除,生成的SQL如下:
接下来在来创建个添加:
这里也用到了version,并且注意SCOPE_IDENTITY();他会返回最后添加的ID号。因为我们这里有返回参数:@CustomerId int = NULL OUTPUT


(
@Version int,
@Firstname nvarchar(50) = NULL,
@Lastname nvarchar(50) = NULL,
@CustomerId int = NULL OUTPUT
)
AS
INSERT INTO [Customer]
(
[Version],
[Firstname],
[Lastname]
)
VALUES
(
@Version,
@Firstname,
@Lastname
)
SELECT @CustomerId = SCOPE_IDENTITY();
RETURN @@Error
然后:四个问好代表4个参数,注意顺序
<sql-insert>exec CustomerInsert ?,?,?,?</sql-insert>
<sql-delete>exec CustomerDelete ?,?</sql-delete>
我们执行添加方法————报错。
可以看到只传过来了3个参数,会报缺少@p3,什么原因呢,看看李永京老师的解答:
--------------------------------
执行CreateCustomerTest()测试方法失败,还是以上问题,是不是生成器问题?
这里,先看看NHibernate中最常用的两个内置生成器:
native:根据底层数据库的能力选择identity、sequence 或者hilo中的一个。
increment:生成类型为Int64、Int16或Int32的标识符,只当没有其他进程同时往同一个表插入数据时,能够保持唯一性。
附:
- identity:对DB2、MySQL、SQL Server、Sybase数据库内置标识字段提供支持。数据库返回的标识符用Convert.ChangeType加以转换,因此能够支持任何整型。
- sequence:在DB2、PostgreSQL、Oracle数据库中使用序列或者Firebird的生成器。数据库返回的标识符用Convert.ChangeType加以转换,因此能够支持任何整型。
- hilo:使用一个高/低位算法高效地生成Int64、Int32或Int16类型的标识符。给定一个表和字段(默认分别是 hibernate_unique_key 和next_hi)作为高位值的来源。高/低位算法生成的标识符只在一个特定的数据库中是唯一的。如果是用户提供的连接,不要使用此生成器。
测试上面方法时,使用NHibernate内置生成器类型native,它根据数据库配置自动选择了identity类型,identity类型对表的主键提供支持,可以为主键插入显式值(标识增量加一)。我们在第一步就是使用NHibernate内置的生成器类型为主键增量加一,没有任何问题,但是看看CustomerInsert存储过程,主键CustomerId使用SCOPE_IDENTITY()插入显式值(标识增量加一),我们就没有必要使用内置的生成器来插入值了,把设置IDENTITY_INSERT为OFF,NHibernate正好提供了increment类型,生成类型仅仅是整型。
-------------------------------------------
可以看出是native生成器的问题,这里有以下几个解决方法:
1.不要SCOPE_IDENTITY();
2.把native改为increment
我们用第二种,即:
<id name="Id" column ="CustomerId">
<generator class ="increment"/>
</id>
这样在试试,可以添加成功了。
修改我就不说了,但是有一点要注意,修改的时候版本会更新,所以Nhibernate会多提供1个参数,旧的version
生成的:
exec CustomerUpdate @p0,@p1,@p2,@p3; @p0 = '2', @p1 = 'YJingCnBlogs', @p2 = 'Lee', @p3 = '15', @p4 = '1'
实际的:
UPDATE [Customer] SET [Version] = '2', [Firstname] = 'YJingCnBlogs', [Lastname] = 'Lee' WHERE [CustomerId] ='1'
可以看到多了个@p4,即旧的版本。
以上就是增删改。最后说下查询。
@i int,
@j int
AS
BEGIN
SELECT @i as value, @j as value2
END
<return-scalar column="value" type="int"/>
<return-scalar column="value2" type="int"/>
exec paramSProcs ?, ?
</sql-query>
其中name名任意,return-scalar为返回的参数。
再在业务层加上:
ParamSProcs为刚才的Name


public IList ParamStoredProcedure()
{
return _session.GetNamedQuery("ParamSProcs")
.SetInt32(0, 10)
.SetInt32(1, 20)
.List();
}
#endregion
进行测试,结果无误。
我们再创建个:
@CustomerId int
AS
BEGIN
SELECT * FROM [Customer]
WHere [CustomerId] =@CustomerId
END
然后配置映射文件


<return class="DomainModel.Entities.Customer,DomainModel">
<return-property name="CustomerId" column="CustomerId"/>
<return-property name ="Version" column="Version"/>
<return-property name="Firstname" column="Firstname"/>
<return-property name="Lastname" column="Lastname"/>
</return>
exec entitySProcs :customerId
</sql-query>
添加业务逻辑:


{
return _session.GetNamedQuery("EntitySProcs")
.SetInt32("customerId", customerId)
.UniqueResult<Customer>();
}
执行——报错:??
为什么,我们先把配置文件改成:
<return class="NHibernateSample.Domain.Entities.Customer,NHibernateSample.Domain"/>
exec entitySProcs :customerId
</sql-query>
再执行——成功。
但我们想只返回一个字段,不要全部呢,没错这时就是用刚才的那种方法:
<return-scalar column="Firstname" type="string" />
exec singleEntitySProcs
</sql-query>
返回Firstname的存储过程。
至此,我们就可以在Nhibernate中使用存储过程和视图了 。