NHibernate Cookbook 学习笔记 5

本文介绍了如何在NHibernate中处理版本和并发问题,包括乐观并发控制和悲观并发控制的实现。此外,还详细讲解了使用Fluent NHibernate进行手动映射的过程,以及如何建立双向一对多的关系,并提供了相应的代码示例。

处理版本和并发问题:

1.修改entity的基本类型,添加一个版本字段

protected virtualint Version { get; set; }

2.在product映射文件中,添加version元素

<natural-id mutable="true">

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

</natural-id>

<version name="Version" />

<property name="Description" />

<property name="UnitPrice" not-null="true" />

 

3.ActorRole的映射文件也添加一个版本元素:

<id name="Id">

  <generator class="guid.comb"/>

</id>

<version name="Version" />

<property name="Actor" not-null="true" />

<property name="Role" not-null="true" />

4.考虑到这种情况,2个用户同时从数据库中取得数据,第一个修改了提交,过了一会第2个修改提交,就覆盖了第一个的修改。处理这种并发的问题有2种方式:

 

乐观并发:第2 个用户提交,会检查这个version,不对就会抛出异常。version这个版本的变化,会在每次跟新数据的时候自动增加

参见这条sql语句:

UPDATE Product

SET    Version = 2 /* @p0 */,

       Name = 'Junk' /* @p1 */,

       Description = 'Cool' /* @p2 */,

       UnitPrice = 100 /* @p3 */

WHERE  Id = '764de11e-1fd0-491e-8158-9db8015f9be5'/* @p4 */

       AND Version = 1 /* @p5 */

悲观并发:锁机制,一旦一个用户取得数据,获得一个独占锁,这个要通过NH的事务 session.Lock方法来做

 

其他的一些乐观并发处理:

<class name="Product" dynamic-update="true" optimistic-lock="dirty">

这种方式生成sql语句如下,只检查被改变的属性:(至于这种放法一定要设置dynamic-update="true")

UPDATE Product

SET    Name = 'Junk' /* @p0 */

WHERE  Id = '741bd189-78b5-400c-97bd-9db80159ef79'/* @p1 */

       AND Name = 'Stuff' /* @p2 */

下面这个配置将检查所有的字段,来确保没有变更:

<class name="Product" dynamic-update="true" optimistic-lock="dirty">

生成的sql如下:

UPDATE Product

SET    Name = 'Junk' /* @p0 */

WHERE  Id = 'd3458d6e-fa28-4dcb-9130-9db8015cc5bb'/* @p1 */

       AND Name = 'Stuff' /* @p2 */

       AND Description = 'Cool' /* @p3 */

       AND UnitPrice = 100 /* @p4 */


代码的方式编写映射文件:

1.添加一个新的文件夹,把之前的5个类拷贝到这个文件夹中,然后再这个文件夹中再添加一个mapping文件夹:注意添加http://fluentnhibernate.org/网站上的,FluentNHibernate.dll,注意这里entity基类中的version字段一定要更改为public的。因为我们的ProductMapping中要用到version这个字段来做映射,protect访问权限会导致找不到这个属性。

2.我们在ProductMapping中添加如下代码,就可以直接映射了:

public class ProductMapping : ClassMap<Product> {
    public ProductMapping() {
        Id(p => p.Id).GeneratedBy.GuidComb();
        DiscriminateSubClassesOnColumn("ProductType");
        Version(p => p.Version);
        NaturalId().Not.ReadOnly().Property(p => p.Name);
        Map(p => p.Description);
        Map(p => p.UnitPrice).Not.Nullable();
    }
}

3.book的映射:

public class BookMapping : SubclassMap<Book> {
    public BookMapping() {
        Map(p => p.Author);
        Map(p => p.ISBN);
    }
}

4.movie的映射:

public class MovieMapping : SubclassMap<Movie> {
        public MovieMapping() {
            Map(m => m.Director);
            HasMany(m => m.Actors).KeyColumn("MovieId").AsList(l => l.Column("ActorIndex"));
        }
}

5.actorrle的映射:

public class ActorRoleMapping : ClassMap<ActorRole> {
    public ActorRoleMapping() {
        Id(ar => ar.Id)
            .GeneratedBy.GuidComb();
        Version(ar => ar.Version);
        Map(ar => ar.Actor)
            .Not.Nullable();
        Map(ar => ar.Role)
            .Not.Nullable();
    }
}

Fluent NHibernate提供2中方式的映射:一种是手动编写映射,一种是自动映射,上面我们都是手动编写的映射,每个类对应一个映射类。

所有的映射类都要继承自ClassMap,而且一个继承体系中的子类要继承自SubclassMap,NaturalId().Not.ReadOnly().Property(p => p.Name);这个和 配置文件中的mutable="true"是一样的效果。所有的属性都用 Map()格式来表示。一对多的属性用 HasMany()来表示,后面可以跟上
 AsMap(), AsBag(), AsSet(), or AsList()这个和配置文件的几个集合类型相对应,AsList可以用Column来指定索引列。

双向的一对多绑定:

如何创建双向的一对多的关系:
1.添加一个类:

namespace ManualRelationships {
    public class Order {
        public virtual Guid Id { get; protected set; }
        public Order() {
            _items = new HashedSet<OrderItem>();
        }
        private Iesi.Collections.Generic.ISet<OrderItem> _items;
        public virtual IEnumerable<OrderItem> Items {
            get {
                return _items;
            }
        }
        public virtual bool AddItem(OrderItem newItem) {
            if (newItem != null && _items.Add(newItem)) {
                newItem.SetOrder(this);
                return true;
            }
            return false;
        }
        public virtual bool RemoveItem(
          OrderItem itemToRemove) {
            if (itemToRemove != null &&
              _items.Remove(itemToRemove)) {
                itemToRemove.SetOrder(null);
                return true;
            }
            return false;
        }
    }
}

2.添加一个订单列表的类:

namespace ManualRelationships {
    public class OrderItem {
        public virtual Guid Id { get; protected set; }
        public virtual Order Order { get; protected set; }
        public virtual void SetOrder(Order newOrder) {
            var prevOrder = Order;
            if (newOrder == prevOrder)
                return;
            Order = newOrder;
            if (prevOrder != null)
                prevOrder.RemoveItem(this);
            if (newOrder != null)
                newOrder.AddItem(this);
        }
    }
}

3.订单的映射文件:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    assembly="ManualRelationships"
    namespace="ManualRelationships">
    <class name="Order" table="`Order`">
        <id name="Id">
            <generator class="guid.comb" />
        </id>
        <set name="Items"
             cascade="all-delete-orphan"
             inverse="true"
             access="field.camelcase-underscore">
            <key column="OrderId" />
            <one-to-many class="OrderItem"/>
        </set>
    </class>
</hibernate-mapping>

4.订单项的映射文件:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    assembly="ManualRelationships"
    namespace="ManualRelationships">
    <class name="OrderItem">
        <id name="Id">
            <generator class="guid.comb" />
        </id>
        <many-to-one name="Order" column="OrderId" />
    </class>
</hibernate-mapping>

如上的配置文件,order这个配置表示order来控制关系,其中用inverse="true"来表示order来维护外键关系。如果要添加一个order,里面有一个orderitem那么生成的sql语句如下:

INSERT INTO "Order" (Id) VALUES (@p0)
INSERT INTO OrderItem (Id) VALUES (@p1)
UPDATE OrderItem SET OrderId = @p0 WHERE Id = @p1

那么如果在orderitem的配置文件中指定:inverse="true"那么将是由orderitem来控制外键关系,得到的语句将会是如下:

INSERT INTO "Order" (Id) VALUES (@p0)
INSERT INTO OrderItem (OrderId, Id) VALUES (@p0, @p1)

有时在属性设置中可能使用到了未初始化的对象,建议是不要用直接的指定这个外键的关系。而是用手动的添加一些方法的方式:
AddItem,RemoveItem,SetOrder这样的方法,来手动指定。注意这个双向的处理,order里面的items要添加项,items里面的order也要添加项,这样就关联起来了。

另外因为order在数据库中是一个关键字,所以这里要用引号括起来。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值