-->
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.  [ 4 posts ] 
Author Message
 Post subject: Map 0 Foreign Key value to Null?
PostPosted: Thu Nov 15, 2007 4:34 pm 
Newbie

Joined: Wed Oct 31, 2007 1:50 pm
Posts: 13
I have a 3rd party database we are trying to expose and there is a foreign key value that instead of being NULL has a value of 0 when there is no corresponding row in the related table.

I have the related table mapped as a many-to-one, but when the value is zero I receive the following error:

Quote:
NHibernate.UnresolvableObjectException: No row with the given identifier exists: 0, of class: Speedway.SpeedyRewards4.Model.Person


We cannot change the way the data is stored as we don't own or manage the client application which places this value in there. Anyone have any ideas?[/quote]


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 15, 2007 5:15 pm 
Newbie

Joined: Tue May 01, 2007 10:20 am
Posts: 9
I had to deal with this myself. The project never panned out, so I'm not sure how fool-proof the plan was, but it seemed to work.

http://forum.hibernate.org/viewtopic.php?t=978393

However, the type system has changed a bit since then, if I recall. I took that code and hacked it up until I got something that seemed to pass my tests. Here was my result...


Code:
namespace JuddNet.Data.FauxNullTypes.Identifier
{
    /// <summary>
    /// see http://forum.hibernate.org/viewtopic.php?t=935678&highlight
    ///     http://forums.hibernate.org/viewtopic.php?t=933785
    ///     http://forum.hibernate.org/viewtopic.php?t=978393
    /// </summary>
    [Serializable]
    public class ZeroAsNullDecimalIdentifierType : AbstractType, IIdentifierType
    {
        // Has to inherite from AbstractType because we need to override NullSafeSet(),
        // which is not exposed in anything "lower" than AbstractType

        private const decimal ZERO = 0;

        #region IIdentifierType Members

        /// <summary>
        ///
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public object StringToObject(string xml)
        {
            object result = FromString(xml);
            if (result != null && ZERO.Equals((decimal)result))
            {
                result = null;
            }
            return result;
        }

        public object FromStringValue(string xml)
        {
            // This method cannot return null

            return decimal.Parse(xml);
        }

        #endregion

        public override object DeepCopy(object val)
        {
            // val would be an decimal, so just returning it would return a copy
            return val;
        }

        public override bool Equals(object x, object y)
        {
            object xTransformed = ((x == null) || ZERO.Equals(x)) ? ZERO : x;
            object yTransformed = ((y == null) || ZERO.Equals(y)) ? ZERO : y;

            return ObjectUtils.Equals(xTransformed, yTransformed);
        }

        /// <include file='IType.cs.xmldoc'
        ///      path='//members[@type="IType"]/member[@name="M:IType.FromString"]/*'
        /// />
        /// <remarks>
        /// <para>
        /// This implementation forwards the call to <see cref="FromStringValue"/> if the parameter
        /// value is not empty.
        /// </para>
        /// <para>
        /// It has been "sealed" because the Types inheriting from <see cref="NullableType"/>
        /// do not need and should not override this method.  All of their implementation
        /// should be in <see cref="FromStringValue"/>.
        /// </para>
        /// </remarks>
        public override sealed object FromString(string xml)
        {
            // Copied from NullableType

            return (xml == null || xml.Length == 0) ? null : FromStringValue(xml);
        }

        public override int GetColumnSpan(IMapping mapping)
        {
            return 1;
        }

        public override bool HasNiceEquals
        {
            get { return true; }
        }

        public override bool IsMutable
        {
            get { return false; }
        }

        public override string Name
        {
            get { return "ZeroAsNullDecimalIdentifierType"; }
        }

        public override sealed object NullSafeGet(IDataReader rs, string name, ISessionImplementor session, object owner)
        {
            // Copied from NullableType
            return NullSafeGet(rs, name);
        }

        public override object NullSafeGet(IDataReader rs, string[] names, ISessionImplementor session, object owner)
        {
            // Copied from NullableType
            return NullSafeGet(rs, names[0]);
        }

        public virtual object NullSafeGet(IDataReader rs, string name)
        {
            int index = rs.GetOrdinal(name);

            if (rs.IsDBNull(index))
            {
                return null;
            }
            else
            {
                decimal fieldValue = Convert.ToDecimal(rs[index]);
                // If it is zero in the database, interpret that as null
                return ZERO.Equals(fieldValue) ? null : (object)fieldValue;
            }
        }

        public override void NullSafeSet(IDbCommand cmd, object value, int index, bool[] settable, ISessionImplementor session)
        {
            NullSafeSet(cmd, value, index, session);
        }

        public override void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session)
        {
            IDataParameter parm = cmd.Parameters[index] as IDataParameter;
            parm.Value = value ?? ZERO;
        }

        public override Type ReturnedClass
        {
            get { return typeof(decimal); }
        }

        public override SqlType[] SqlTypes(IMapping mapping)
        {
            return new SqlType[] {
                                     SqlTypeFactory.Decimal
                                 };
        }

        public override string ToLoggableString(object value, ISessionFactoryImplementor factory)
        {
            if (value != null)
            {
                return value.ToString();
            }
            else
            {
                return "null-decimal";
            }
        }

        public override bool IsDirty(object old, object current, bool[] checkable, ISessionImplementor session)
        {
            // TODO: this overload of IsDirty is new in NHibernate 1.2.  Don't know what the "checkable"
            // parameter is, so we just call the other IsDirty for now.
            return IsDirty(old, current, session);
        }
    }
}


For your entity that is your aggregate (which I believe in your case is Person), you would use this type for the identifier type...


Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain">
  <class name="Person" table="PersonTable">
    <id
      name="Id"
      column="PersonId"
      type="JuddNet.Data.FauxNullTypes.Identifier.ZeroAsNullDecimalIdentifierType, JuddNet.Data">
      <generator class="assigned"/>
    </id>

    <property name="Name" column="CNme" type="string" length="30"/>

  </class>
</hibernate-mapping>


Here's what I had in the entity where Person is an aggregate...

Code:
<many-to-one
            name="Person" column="PersonFk"
            class="Domain.Person"
            not-null="false" not-found="ignore"/>


Hope this at least gets you started.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 15, 2007 9:12 pm 
Newbie

Joined: Wed Oct 31, 2007 1:50 pm
Posts: 13
Wow - thanks for such detail. I'll give it a try in the morning and let you know what I find. Great stuff.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 16, 2007 10:32 am 
Newbie

Joined: Wed Oct 31, 2007 1:50 pm
Posts: 13
Worked great! Thanks a TON! That saved me soooo much time and research.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 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.