These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Using NHibernate with the Strategy Pattern
PostPosted: Wed Oct 15, 2008 5:32 am 
Newbie

Joined: Wed Oct 15, 2008 5:15 am
Posts: 4
Hello,

I am trying to persist a value that will allow me to create the correct ShippingStrategy when the Order entity is retrieved from the database (see class diagram below). I have done this loads of times before but just not with NHibernate, surely there must be way.

Image

I have got it to work using the one database table, although I have had to alter my domain model to fit in with NHibernate so its lost is POCO :0(

Code:
public class Order
{
        private IShipping _ShippingStrategy;
        private string _ShippingType;
        private int _OrderID;
        public Order() { }

        public int OrderID
        {
             get { return _OrderID; }
             set { _OrderID = value; }
        }
        // Here only because of NHibernate :0(
        // This is the value that gets stored in the database
        private string ShippingType
        { 
           get { return _ShippingType; }
           set { _ShippingType = value; }
        }

        public IShipping Shipping
        {
           get {
                     if (_ShippingStrategy == null)
                        // The first time we try and access the ShippingStrategy after we have retrieved the order entity from
                        // the database it will be null, so we will have to create the concrete class by using the ShippingType that
                        // we have retrieved from the database
                         {
                            // Will refactor this to a factory method
                            switch (ShippingType)
                               {
                                  case "RoyalMail":
                                        _ShippingStrategy = new RoyalMail();
                                        break;
                                  case "FedEx":
                                        _ShippingStrategy = new FedEx();
                                        break;
                               }
                          }
                     
                     return _ShippingStrategy;
               }
         set
               {
                  _ShippingStrategy = value;
                  // Here only because of NHibernate
                 // Update the value that we are going to store in the database.
                  _ShippingType = _ShippingStrategy.type;
               }
     }
}

My order mapping file:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Order" assembly="OrderTest.Model">
       <class name="OrderTest.Model.Order" table="Order" lazy="false">

                 <id name="ID" column="id" type="Int32" unsaved-value="0">
                        <generator class="native" />
                 </id>
                 <property name="ShippingType">
                        <column name="ShippingType" length="10" not-null="true" />
                 </property>

        </class>
</hibernate-mapping>


My order sql server Database Table with the columns:

- ID
- ShippingType

As you can see from the above this works but I have complicated my Domain Model to get NHibernate to work. Is there a better way to get this to work with NHibernate?

Thanks for your help
Scott


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 15, 2008 5:13 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
I haven't used it, but I think there's a way to specify that a property maps to a Type. NHibernate persists it as the assembly qualified type name, and your entity's property ends up with the actual type. You can then use that Type property in your lazy-initialize getter for your strategy property to cleanly instantiate a new instance without a smelly switch statement or a call to a strategy factory.

Otherwise you could define a child table for order strategies which uses a discriminator column to determine which (sub)class of your strategy to instantiate. We don't use inheritance that way with NHibernate, but my understanding of the feature is that it would do what you want. I think you would need a base class for your strategy though, possibly a non-abstract one; an interface may not be enough to satisfy NHibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 17, 2008 8:17 am 
Newbie

Joined: Wed Oct 15, 2008 5:15 am
Posts: 4
Yeah I found this blog post about mapping the strategy to a type:

http://priyanshugoyal.wordpress.com/200 ... e-mapping/

it seems to be exactly what I am after. I just need to convert it from java and work out how it fits in with NHibernate. I don't suppose you know any good resources about UserTypes do you?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 17, 2008 11:07 am 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
We've done some trivial UserTypes, and even for those we had to guess what to do from examples we scraped up ...


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 17, 2008 11:28 am 
Newbie

Joined: Wed Oct 15, 2008 5:15 am
Posts: 4
I found another post on creating my own UserType

http://www.lostechies.com/blogs/rhousto ... rtype.aspx

My Entity can be nice and POCO now:

public class Order
{
private IShipping _ShippingStrategy;
private int _OrderID;

public Order()
{ }
public int OrderID
{
get { return _OrderID; }
set { _OrderID = value; }
}

public IShipping Shipping
{
get { return _ShippingStrategy; }
set { _ShippingStrategy = value; }
}
}

and the mapping file is nice and straightforward:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Order" assembly="OrderTest.Model">
<class name="OrderTest.Model.Order" table="Order" lazy="false">

<id name="ID" column="id" type="Int32" unsaved-value="0">
<generator class="native" />
</id>

<property name="Shipping" type="OrderTest.Infrastructure.Repositories.ShippingMapper, OrderTest.Infrastructure.Repositories" column="Shipping" not-null="true" />

</class>

</hibernate-mapping>

With the ShippingMapper doing all the dirty work.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 17, 2008 1:56 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Please post your UserType if you don't mind, so that we can follow the example. Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Sat Nov 15, 2008 10:41 am 
Newbie

Joined: Wed Oct 15, 2008 5:15 am
Posts: 4
Sorry for the late reply. This is the code I used:

Public Class ShippingUserType
Implements NHibernate.UserTypes.IUserType

Public Function Assemble(ByVal cached As Object, ByVal owner As Object) As Object Implements NHibernate.UserTypes.IUserType.Assemble
Return cached
End Function

Public Function DeepCopy(ByVal value As Object) As Object Implements NHibernate.UserTypes.IUserType.DeepCopy
Return value
End Function

Public Function Disassemble(ByVal value As Object) As Object Implements NHibernate.UserTypes.IUserType.Disassemble
Return value
End Function

Public Function Equals1(ByVal x As Object, ByVal y As Object) As Boolean Implements NHibernate.UserTypes.IUserType.Equals
Return True
End Function

Public Function GetHashCode1(ByVal x As Object) As Integer Implements NHibernate.UserTypes.IUserType.GetHashCode
Return If(x Is Nothing, GetType(Boolean).GetHashCode() + 473, x.GetHashCode())
End Function

Public ReadOnly Property IsMutable() As Boolean Implements NHibernate.UserTypes.IUserType.IsMutable
Get
Return False
End Get
End Property

Public Function NullSafeGet(ByVal rs As System.Data.IDataReader, ByVal names() As String, ByVal owner As Object) As Object Implements NHibernate.UserTypes.IUserType.NullSafeGet

Dim countOf As Integer = rs.FieldCount
Dim ShippingType As String
Dim i As Integer = 0

While i < countOf
ShippingType = rs(i)
i += 1
End While


ShippingType = rs(3)
Dim ShippingStrategy As IShipping

ShippingStrategy = ShippingFactory.GetShipping(ShippingType)

Return ShippingStrategy
End Function

Public Sub NullSafeSet(ByVal cmd As System.Data.IDbCommand, ByVal value As Object, ByVal index As Integer) Implements NHibernate.UserTypes.IUserType.NullSafeSet

If value Is Nothing Then
DirectCast(cmd.Parameters(index), IDataParameter).Value = DBNull.Value
Else
Dim ShippingStrategy As IShipping= CType(value, IShipping).Type

DirectCast(cmd.Parameters(index), IDataParameter).Value = ShippingStrategy.Name
End If

End Sub

Public Function Replace(ByVal original As Object, ByVal target As Object, ByVal owner As Object) As Object Implements NHibernate.UserTypes.IUserType.Replace
Return original
End Function

Public ReadOnly Property ReturnedType() As System.Type Implements NHibernate.UserTypes.IUserType.ReturnedType
Get
Return GetType(ShippingUserType)
End Get
End Property

Public ReadOnly Property SqlTypes() As NHibernate.SqlTypes.SqlType() Implements NHibernate.UserTypes.IUserType.SqlTypes
Get
Dim sql_types As NHibernate.SqlTypes.SqlType() = {New NHibernate.SqlTypes.StringSqlType}

Return sql_types
End Get
End Property
End Class

I hope you find this useful. I think there is probably an easier way to do this though, I just need to find it.....


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.