NHIbernate学习之旅【八】—— 视图/存储过程(转http://www.cnblogs.com/neekey/archive/2010/05/17/1737215.html)

Coordinator
Oct 20, 2011 at 4:08 PM

  这篇讲讲NHibernate的视图和存储过程的使用方法。

  视图在Nhibernate中使用比较简单,这里大概讲解下:

  首先创建一个视图查询CustomerId、Firstname、Lastname、OrderId、OrderDate字段。命名viewCustomer

  viewCustomer可以看做成一张实体表,所以接下来就和普通表一样在Nhibernate中设置就行了。

  实体类

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->    public class CustomerView
    {
        
public virtual int CustomerId { getprivate set; }
        
public virtual string Firstname { getprivate set; }
        
public virtual string Lastname { getprivate set; }
        
public virtual int OrderId { getprivate set; }
        
public virtual DateTime OrderDate { getprivate set; }
    }
  

 

  映射文件CustomerView.hbm.xml

  

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><?xml version="1.0" encoding="utf-8" ?>
<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>

 

  设置完后和普通的表使用方法一致:

  

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->public IList<CustomerView> GetCustomerView(DateTime orderDate)
{
    
return _session.CreateCriteria(typeof(CustomerView))
        .Add(Restrictions.Gt(
"OrderDate", orderDate))
        .List
<CustomerView>();
}

  

  这样就完成了视图的配置,很简单。

  接下来是存储过程,这个比较麻烦,并且不符合OO。

  首先我们来创建个简单的:

  

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->ALTER PROCEDURE [dbo].[CustomerDelete]
(
    
@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执行的存储过程,?代表参数,有几个参数就写几个。

  不过注意一点,如果还有其他的增,删的话,要按一定顺序写,如下,不然会报错。

  

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><?xml version="1.0" encoding="utf-8" ?> 
<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

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->    ALTER PROCEDURE [dbo].[CustomerInsert]
(
    
@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,即旧的版本。

 

  以上就是增删改。最后说下查询。

  

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->    CREATE PROCEDURE paramSProcs
     
@i int,
     
@j int
AS
BEGIN
    
SELECT @i as value, @j as value2
END

 

 

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->    <sql-query name="ParamSProcs">
        
<return-scalar column="value" type="int"/>
        
<return-scalar column="value2" type="int"/>
        exec paramSProcs ?, ?
    
</sql-query>

 

其中name名任意,return-scalar为返回的参数。

  再在业务层加上:

  ParamSProcs为刚才的Name

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->        #region 存储过程
        
public IList ParamStoredProcedure()
        {
            
return _session.GetNamedQuery("ParamSProcs")
                .SetInt32(
010)
                .SetInt32(
120)
                .List();
        }
        
#endregion

 

  进行测试,结果无误。

  我们再创建个:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->  CREATE PROCEDURE [entitySProcs]
  
@CustomerId int
AS
BEGIN
   
SELECT * FROM [Customer]
   
WHere [CustomerId] =@CustomerId
END

 

 

  然后配置映射文件

  

 

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><sql-query name="EntitySProcs">
  
<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>

 

  添加业务逻辑:

  

代码
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->        public Customer EntityStoredProcedure(int customerId)
        {
            
return _session.GetNamedQuery("EntitySProcs")
                .SetInt32(
"customerId", customerId)
                .UniqueResult
<Customer>();
        }

 

  执行——报错:??

  为什么,我们先把配置文件改成:

  

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->    <sql-query name="EntitySProcs">
        
<return class="NHibernateSample.Domain.Entities.Customer,NHibernateSample.Domain"/>
        exec entitySProcs :customerId
    
</sql-query>

 

  再执行——成功。

  但我们想只返回一个字段,不要全部呢,没错这时就是用刚才的那种方法:

  

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><sql-query name="SingleEntitySProcs">
  
<return-scalar column="Firstname" type="string" />
  exec singleEntitySProcs
</sql-query>

 

返回Firstname的存储过程。

 

  至此,我们就可以在Nhibernate中使用存储过程和视图了 。