解读Petshop3.2用Nhibernate重构系列(四)

这一节,我们来讲述如何实现一个一对多对象的操作。很显然的,在PetShop中最显著的就是Order和LineItem之间的关系了。
我们应该把Order对象和LineItem对象的保存处理在一个事务中。
在这一节,他利用了通过一个回调函数提供一个事务环境(TransHelper.cs)。
首先我们看看这些配置文件
<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping  xmlns ="urn:nhibernate-mapping-2.0" >
    
< class  name ="PetShop.BLL.Order, PetShop.BLL"  table ="Orders" >
        
< id  name ="OrderId"  column ="OrderId"  type ="Int32"  unsaved-value ="0"  access ="nosetter.camelcase-underscore" >
            
< generator  class ="native" />
        
</ id >
        
< property  type ="String"  length ="20"  name ="UserId"  column ="UserId"  not-null ="true" />
        
< property  type ="DateTime"  not-null ="true"  column ="OrderDate"  name ="Date" />
        
< component  name ="CreditCard"  class ="PetShop.BLL.CreditCard, PetShop.BLL" >
            
< property  name ="CardNumber"  type ="String"  length ="20"  column ="CreditCard"  not-null ="true" />
            
< property  name ="CardExpiration"  column ="ExprDate"  type ="String"  length ="7"  not-null ="true" />
            
< property  name ="CardType"  column ="CardType"  length ="40"  not-null ="true"  type ="String" />
        
</ component >
        
< component  name ="BillingAddress"  class ="PetShop.BLL.Address, PetShop.BLL" >
            
< property  name ="Address1"  column ="BillAddr1"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="Address2"  column ="BillAddr2"  type ="String"  length ="80"  not-null ="false" />
            
< property  name ="City"  column ="BillCity"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="State"  column ="BillState"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="Zip"  column ="BillZip"  type ="String"  length ="20"  not-null ="true" />
            
< property  name ="Country"  column ="BillCountry"  type ="String"  length ="20"  not-null ="true" />
            
< property  name ="FirstName"  column ="BillToFirstName"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="LastName"  column ="BillToLastName"  type ="String"  length ="80"  not-null ="true" />
        
</ component >
        
< component  name ="ShippingAddress"  class ="PetShop.BLL.Address, PetShop.BLL" >
            
< property  name ="Address1"  column ="ShipAddr1"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="Address2"  column ="ShipAddr2"  type ="String"  length ="80"  not-null ="false" />
            
< property  name ="City"  column ="ShipCity"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="State"  column ="ShipState"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="Zip"  column ="ShipZip"  type ="String"  length ="20"  not-null ="true" />
            
< property  name ="Country"  column ="ShipCountry"  type ="String"  length ="20"  not-null ="true" />
            
< property  name ="FirstName"  column ="ShipToFirstName"  type ="String"  length ="80"  not-null ="true" />
            
< property  name ="LastName"  column ="ShipToLastName"  type ="String"  length ="80"  not-null ="true" />
        
</ component >
        
< property  name ="OrderTotal"  type ="Decimal"  column ="TotalPrice"  not-null ="true" />
        
< property  name ="Courier"  type ="String"  column ="Courier"  not-null ="true"  length ="80"  access ="field.camelcase-underscore" />
        
< property  name ="Locale"  type ="String"  column ="Locale"  not-null ="true"  length ="20"  access ="field.camelcase-underscore" />
        
< bag  name ="LineItems"  access ="nosetter.camelcase-underscore"  inverse ="true"  cascade ="save-update" >
            
< key  column ="OrderId" />
            
< one-to-many  class ="PetShop.BLL.CartItem, PetShop.BLL" />
        
</ bag >
        
< one-to-one  name ="Status"  class ="PetShop.BLL.OrderStatus, PetShop.BLL"  cascade ="save-update" />
    
</ class >
</ hibernate-mapping >
包含了属性/组件/bag :)我只是知道大概,但是不知道怎么表达,没什么仔细研究:)
最重要的是bag--它可以用来指定一个一对多的关系。

接着我们来看接口
using  System;

// References to PetShop specific libraries
// PetShop busines entity library
using  PetShop.BLL;

namespace  PetShop.IDAO {
    
/// <summary>
    
/// Interface for the Order DAL
    
/// </summary>

    public interface IOrderDAO{
        
/// <summary>
        
/// Method to insert an order header
        
/// </summary>
        
/// <param name="order">Business entity representing the order</param>

        void Insert(Order order);

        
/// <summary>
        
/// Reads the order information for a given orderId
        
/// </summary>
        
/// <param name="orderId">Unique identifier for an order</param>
        
/// <returns>Business entity representing the order</returns>

        Order GetOrder(int orderId);
    }

}

 
    这里定义了新增订单/根据订单ID获取订单的操作规范。
同时我们还需要看看IInventoryDAO接口

using  System;

using  PetShop.BLL;

namespace  PetShop.IDAO {
    
/// <summary>
    
/// Interface for the Inventory DAL
    
/// </summary>

    public interface IInventoryDAO{
        
/// <summary>
        
///  Reduces the stock level by the given quantity for items in an order
        
/// </summary>
        
/// <param name="inventory"></param>

        void TakeStock(Inventory inventory);
    }

}


这个定义了减少库存的操作。
那么,接下来,我们可以来看看实体类

using  System;
using  System.Collections;
using  PetShop.Helper;
using  PetShop.IDAO;

namespace  PetShop.BLL {
    
/// <summary>
    
/// Business entity used to model an order
    
/// </summary>

    [Serializable]
    
public class Order{
        
// These variables are used to demonstrate the rollback characterisitic 
        
// of distributed transactions and would not form part of a production application
        private const string ACID_USER_ID = "ACID";
        
private const string ACID_ERROR_MSG = "ACID test exception thrown for distributed transaction!";

        
private int _orderId;
        
private DateTime _date;
        
private string _userId;
        
private CreditCard _creditCard;
        
private Address _billingAddress;
        
private Address _shippingAddress;
        
private decimal _orderTotal;
        
private string _courier = "UPS";
        
private string _locale = "US_en";
        
private IList _lineItems;
        
private OrderStatus _status;

        
public int OrderId{
            
get return _orderId; }
        }


        
public DateTime Date{
            
get return _date; }
            
set { _date = value; }
        }


        
public string UserId{
            
get return _userId; }
            
set { _userId = value; }
        }


        
public CreditCard CreditCard{
            
get return _creditCard; }
            
set { _creditCard = value; }
        }


        
public Address BillingAddress{
            
get return _billingAddress; }
            
set { _billingAddress = value; }
        }


        
public Address ShippingAddress{
            
get return _shippingAddress; }
            
set { _shippingAddress = value; }
        }


        
public decimal OrderTotal{
            
get return _orderTotal; }
            
set { _orderTotal = value; }
        }


        
public IList LineItems {
            
get return _lineItems; }
            
set 
                
int lineNum = 1;
                
foreach(CartItem item in value){
                    item.LineNum 
= lineNum++;
                    item.Order   
= this;
                }

                _lineItems 
= value;
            }

        }

        
        
public OrderStatus Status {
            
get return _status; } 
            
set {
                _status 
= value;
                _status.Order 
= this;
            }

        }


        
public static Order Load(int orderId) {
            
// Validate input
            if (orderId < 1return null;

            
// Return the order from the DAL
            return ((IOrderDAO)ObjectFactory.GetInstance("OrderDAO")).GetOrder(orderId);
        }

        
        
public void Insert(){
            IManagedTransactionContext mtc 
= new TransHelper();
            mtc.DoCallback( 
new ContextCallback(InsertInTransaction) ); 
        }


        
/// <summary>
        
/// A method to insert a new order into the system
        
/// The orderId will be generated within the method and should not be supplied
        
/// As part of the order creation the inventory will be reduced by the quantity ordered
        
/// </summary>
        
/// <param name="order">All the information about the order</param>

        private void InsertInTransaction(){
            
            
// Call the insert method in the DAL to insert the header
            this.Status = new OrderStatus();

            ((IOrderDAO)ObjectFactory.GetInstance(
"OrderDAO")).Insert(this);
            
            
foreach(CartItem item in LineItems){
                item.TakeStock();
            }

        
            
// As part of the sample application we have created a user 
            
// you can tested distributed transactions with
            
// If the order has been created with the user 'Acid', 
            
// then throw an exception which will rollback the entire transaction
            if (this.UserId == ACID_USER_ID) throw new ApplicationException(ACID_ERROR_MSG);
        }

    }

}
我们在这里可以看到对于这种操作,他得通过一个回调函数来显式的控制事务。
这样我们就可以不用考虑那么多了,只需要考虑操作步骤。
   1.给Order对象的Status赋值
   2.通过对象工厂获取一个OrderDAO的借口,并执行新增Order操作。
     在这个步骤,Order LineItem OrderStatus被执行了插入操作。
   3.我们需要扣除已经销售的库存
      我们遍历LineItems属性,调用对应的扣除库存的操作

完成了。其实也不难,只是不知道方法而已:)

你可能感兴趣的:(Hibernate)