处理版本和并发问题:
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在数据库中是一个关键字,所以这里要用引号括起来。