-->
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.  [ 3 posts ] 
Author Message
 Post subject: Map a formula in a named query? Value cannot be null
PostPosted: Fri Jan 25, 2008 12:16 pm 
Regular
Regular

Joined: Wed Jun 21, 2006 3:13 pm
Posts: 110
I apologize in advance if this has already been covered. I wasn't able to find anything when I searched the forums, though... so here goes.


Here's a summary of the problem that I'm encountering. I have a mapping for Kennel. Kennel has two properties derived from formulas, ReviewCount and RatingScore. When I attempt to load a Kennel by id, it works fine and the SQL looks as I would expect it.

However, when I attempt to load a list from a named query, I receive the error "value cannot be null. Parameter name: fieldName". Stepping through the code shows me that the NHibernate loader is passing a null column name into the data reader.

I originally did not specify a column name for the derived property mapping. I have since added the column names and ensured they match the column names for the formulas on my named query. I assume I've botched something up in the middle somewhere...

Hibernate version:
NHibernate 1.2.0.4

Mapping documents:

Here is a (slightly trimmed down, removed the bags) copy of the mapping document:

Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false">
   <class name="KennelFinder.Kennel, KennelFinder" table="Kennel">
      <id name="Id" type="Int32" access="nosetter.camelcase" >
         <column name="kennelId" not-null="true" unique="true" />
         <generator class="native" />
      </id>

      <version name="Version" column="version" access="nosetter.camelcase" />

      <property name="Name" column="Name" />
      <property name="Address1" column="Address1" />
      <property name="Address2" column="Address2" />
      <property name="City" column="City" />
      <property name="State" column="State" />
      <property name="ZipCode" column="postal" />
      <property name="InsertDate" column="InsertDate" />
      <property name="LastUpdateDate" column="LastUpdateDate" />
      <property name="LastEmailNotice" column="LastEmailNotice" />
      <property name="SearchRank" column="SearchRank" />
      <property name="IsAbkaMember" column="abkaMember" />
      <property name="Status" column="Status" type="KennelFinder.KennelStatus, KennelFinder" />
      <property name="IsVerified" column="IsVerified" />
      <property name="ReviewCount" column="ReviewCount" formula="(select count(r.kennelid) from rating r where r.kennelid = kennelId)"  update="false" insert="false"></property>
      <property name="RatingScore" column="RatingScore" formula="isnull((select avg(r.rating) from rating r where r.kennelid = kennelId),0)"  update="false" insert="false"></property>
   </class>

   <sql-query name="GetKennelListByVicinity">
      <return alias="Kennels" class="KennelFinder.Kennel, KennelFinder" lock-mode="none" />
      <![CDATA[
      SET NOCOUNT ON;

         declare @zipcode char(5)
         declare @latitude decimal(9,6)
         declare @longitude decimal(9,6)

         create table #zipCodes (zipcode char(5))

         DECLARE ZipCode_Cursor CURSOR LOCAL FAST_FORWARD FOR
         select zipcode, latitude, longitude from geoinformation where city = :city and statecode = :state

         open ZipCode_Cursor

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude

         while @@FETCH_STATUS = 0
         begin
         insert into #zipCodes values (@zipCode)

         insert #zipCodes
         select distinct zipCode from geoinformation g, radius(@latitude, @longitude, :radius) rad
         where g.latitude >= rad.minLatitude and g.latitude <= rad.maxLatitude and g.longitude >= rad.minLongitude and g.longitude <= rad.maxLongitude

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude
         end

         close ZipCode_Cursor
         deallocate ZipCode_Cursor

      SELECT k.*,
      (select count(r.kennelid) from rating r where r.kennelid = k.kennelId) as ReviewCount,
      isnull((select avg(r.rating) from rating r where r.kennelid = k.kennelId),0) as RatingScore
      FROM [mobyWeb].[dbo].[Kennel] k
      where
            k.postal in (select distinct zipcode from #zipcodes)
      order by
         k.name
    drop table #zipCodes
    ]]>
   </sql-query>
</hibernate-mapping>


The two lines I believe to be the offending lines are:

Code:
      <property name="ReviewCount" column="ReviewCount" formula="(select count(r.kennelid) from rating r where r.kennelid = kennelId)"  update="false" insert="false"></property>
      <property name="RatingScore" column="RatingScore" formula="isnull((select avg(r.rating) from rating r where r.kennelid = kennelId),0)"  update="false" insert="false"></property>


The code that calls this is:

Code:
public static List<Kennel> FindByCityVicinity(string city, string state, short radius)
{
   IQuery vicinityQuery = DbSessionContext.Current.Session.GetNamedQuery("GetKennelListByVicinity");
   vicinityQuery.SetString("city", city);
   vicinityQuery.SetString("state", state);
   vicinityQuery.SetInt16("radius", radius);

   return vicinityQuery.List<Kennel>() as List<Kennel>;
}



Full stack trace of any exception that occurs:

Code:
[ArgumentNullException: Value cannot be null.
Parameter name: fieldName]
   System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName) +1360553
   System.Data.SqlClient.SqlDataReader.GetOrdinal(String name) +102
   NHibernate.Driver.NHybridDataReader.GetOrdinal(String name) +10
   NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name) +33
   NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner) +25
   NHibernate.Type.AbstractType.Hydrate(IDataReader rs, String[] names, ISessionImplementor session, Object owner) +19
   NHibernate.Loader.Loader.Hydrate(IDataReader rs, Object id, Object obj, ILoadable persister, ISessionImplementor session, String[][] suffixedPropertyColumns) +227
   NHibernate.Loader.Loader.LoadFromResultSet(IDataReader rs, Int32 i, Object obj, Type instanceClass, EntityKey key, LockMode lockMode, ILoadable rootPersister, ISessionImplementor session) +224
   NHibernate.Loader.Loader.InstanceNotYetLoaded(IDataReader dr, Int32 i, ILoadable persister, EntityKey key, LockMode lockMode, EntityKey optionalObjectKey, Object optionalObject, IList hydratedObjects, ISessionImplementor session) +153
   NHibernate.Loader.Loader.GetRow(IDataReader rs, ILoadable[] persisters, EntityKey[] keys, Object optionalObject, EntityKey optionalObjectKey, LockMode[] lockModes, IList hydratedObjects, ISessionImplementor session) +271
   NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) +178
   NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +496
   NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +56
   NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) +29

[ADOException: could not execute query
[ SET NOCOUNT ON;

         declare @zipcode char(5)
         declare @latitude decimal(9,6)
         declare @longitude decimal(9,6)

         create table #zipCodes (zipcode char(5))

         DECLARE ZipCode_Cursor CURSOR LOCAL FAST_FORWARD FOR
         select zipcode, latitude, longitude from geoinformation where city = ? and statecode = ?

         open ZipCode_Cursor

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude

         while @@FETCH_STATUS = 0
         begin
         insert into #zipCodes values (@zipCode)

         insert #zipCodes
         select distinct zipCode from geoinformation g, radius(@latitude, @longitude, ?) rad
         where g.latitude >= rad.minLatitude and g.latitude <= rad.maxLatitude and g.longitude >= rad.minLongitude and g.longitude <= rad.maxLongitude

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude
         end

         close ZipCode_Cursor
         deallocate ZipCode_Cursor

      SELECT k.*,
      (select count(r.kennelid) from rating r where r.kennelid = k.kennelId) as ReviewCount,
      isnull((select avg(r.rating) from rating r where r.kennelid = k.kennelId),0) as RatingScore
      FROM [mobyWeb].[dbo].[Kennel] k
      where
            k.postal in (select distinct zipcode from #zipcodes)
      order by
         k.name
    drop table #zipCodes ]
  Name: state - Value: MN
  Name: radius - Value: 25
  Name: city - Value: Eden Prairie
[SQL: SET NOCOUNT ON;

         declare @zipcode char(5)
         declare @latitude decimal(9,6)
         declare @longitude decimal(9,6)

         create table #zipCodes (zipcode char(5))

         DECLARE ZipCode_Cursor CURSOR LOCAL FAST_FORWARD FOR
         select zipcode, latitude, longitude from geoinformation where city = ? and statecode = ?

         open ZipCode_Cursor

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude

         while @@FETCH_STATUS = 0
         begin
         insert into #zipCodes values (@zipCode)

         insert #zipCodes
         select distinct zipCode from geoinformation g, radius(@latitude, @longitude, ?) rad
         where g.latitude >= rad.minLatitude and g.latitude <= rad.maxLatitude and g.longitude >= rad.minLongitude and g.longitude <= rad.maxLongitude

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude
         end

         close ZipCode_Cursor
         deallocate ZipCode_Cursor

      SELECT k.*,
      (select count(r.kennelid) from rating r where r.kennelid = k.kennelId) as ReviewCount,
      isnull((select avg(r.rating) from rating r where r.kennelid = k.kennelId),0) as RatingScore
      FROM [mobyWeb].[dbo].[Kennel] k
      where
            k.postal in (select distinct zipcode from #zipcodes)
      order by
         k.name
    drop table #zipCodes]]
   NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) +109
   NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet querySpaces, IType[] resultTypes) +78
   NHibernate.Impl.SessionImpl.ListCustomQuery(ICustomQuery customQuery, QueryParameters queryParameters, IList results) +99
   NHibernate.Impl.SessionImpl.List(NativeSQLQuerySpecification spec, QueryParameters queryParameters) +136
   NHibernate.Impl.SqlQueryImpl.List() +214
   KennelFinder.KennelDataAccess.FindByCityVicinity(String city, String state, Int16 radius) in C:\oms\KennelFinder\src\KennelFinder\KennelDataAccess.cs:29
   KennelFinder.Web.SearchResults.ShowResultList(String address, String city, String state, Int16 radius, Double latitude, Double longitude) in C:\oms\KennelFinder\src\KennelFinder.Web\SearchResults.aspx.cs:67
   KennelFinder.Web.SearchResults.Page_Load(Object sender, EventArgs e) in C:\oms\KennelFinder\src\KennelFinder.Web\SearchResults.aspx.cs:59
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +15
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +34
   System.Web.UI.Control.OnLoad(EventArgs e) +99
   System.Web.UI.Control.LoadRecursive() +47
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1061


Again, adding in the NHibernate source and stepping through, the issue comes down to the fact that, somehow, a null column name is being passed in to the data reader. I think I've isolated it to the two formula mappings because everything works fine when I comment those two lines out of my mapping document.

Name and version of the database you are using:

SQL Server 2005 with the SQL Server 2005 dialect


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 25, 2008 10:20 pm 
Regular
Regular

Joined: Wed Jun 21, 2006 3:13 pm
Posts: 110
Ok, the original post is still causing me an error. However, I do have a work-around that doesn't make me feel too dirty. I'm specifying a query for the loader now. I can derive my calculations in that loader rather than relying on the formula attribute of my property element.

My mapping file is now:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false">
   <class name="KennelFinder.Kennel, KennelFinder" table="Kennel">
      <id name="Id" type="Int32" access="nosetter.camelcase" >
         <column name="kennelId" not-null="true" unique="true" />
         <generator class="native" />
      </id>

      <version name="Version" column="version" access="nosetter.camelcase" />

      <property name="Name" column="Name" />
      <property name="Address1" column="Address1" />
      <property name="Address2" column="Address2" />
      <property name="City" column="City" />
      <property name="State" column="State" />
      <property name="ZipCode" column="postal" />
      <property name="InsertDate" column="InsertDate" />
      <property name="Latitude" column="Latitude" />
      <property name="Longitude" column="Longitude" />
      <property name="LastUpdateDate" column="LastUpdateDate" />
      <property name="LastEmailNotice" column="LastEmailNotice" />
      <property name="SearchRank" column="SearchRank" />
      <property name="IsAbkaMember" column="abkaMember" />
      <property name="Status" column="Status" type="KennelFinder.KennelStatus, KennelFinder" />
      <property name="IsVerified" column="IsVerified" />

      <property name="ReviewCount" column="ReviewCount" update="false" insert="false"></property>
      <property name="RatingScore" column="RatingScore" update="false" insert="false"></property>

      <loader query-ref="GetKennelById"/>
   </class>

   <sql-query name="GetKennelById">
      <return alias="Kennels" class="KennelFinder.Kennel, KennelFinder" lock-mode="none" />
      <![CDATA[
      SET NOCOUNT ON;

      SELECT k.*,
      (select count(r.kennelid) from rating r where r.kennelid = k.kennelId) as ReviewCount,
      isnull((select avg(r.rating) from rating r where r.kennelid = k.kennelId),0) as RatingScore
      FROM [mobyWeb].[dbo].[Kennel] k
      where
            k.kennelid=?
      order by
         k.name
    ]]>
   </sql-query>
   
   <sql-query name="GetKennelListByVicinity">
      <return alias="Kennels" class="KennelFinder.Kennel, KennelFinder" lock-mode="none" />
      <![CDATA[
      SET NOCOUNT ON;

         declare @zipcode char(5)
         declare @latitude decimal(9,6)
         declare @longitude decimal(9,6)

         create table #zipCodes (zipcode char(5))

         DECLARE ZipCode_Cursor CURSOR LOCAL FAST_FORWARD FOR
         select zipcode, latitude, longitude from geoinformation where city = :city and statecode = :state

         open ZipCode_Cursor

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude

         while @@FETCH_STATUS = 0
         begin
         insert into #zipCodes values (@zipCode)

         insert #zipCodes
         select distinct zipCode from geoinformation g, radiusassistant(@latitude, @longitude, :radius) rad
         where g.latitude >= rad.minLatitude and g.latitude <= rad.maxLatitude and g.longitude >= rad.minLongitude and g.longitude <= rad.maxLongitude

         fetch next from ZipCode_Cursor
         into @zipcode, @latitude, @longitude
         end

         close ZipCode_Cursor
         deallocate ZipCode_Cursor

      SELECT k.*,
      (select count(r.kennelid) from rating r where r.kennelid = k.kennelId) as ReviewCount,
      isnull((select avg(r.rating) from rating r where r.kennelid = k.kennelId),0) as RatingScore
      FROM [mobyWeb].[dbo].[Kennel] k
      where
            k.postal in (select distinct zipcode from #zipcodes)
    drop table #zipCodes
    ]]>
   </sql-query>
</hibernate-mapping>



The only other change is that, in another section of code, I was using a Criteria to pull back kennels by certain filter values (for one of my admin screens). I now dynamically create a SQL query instead (it should be easy to visualize how this looked with a Criteria and Expressions before)

Code:
      public static IList<Kennel> FindByFilter(KennelFilter filter)
      {
         string sql = "select k.*, (select count(r.kennelid) from rating r where r.kennelid = k.kennelId) as ReviewCount, isnull((select avg(r.rating) from rating r where r.kennelid = k.kennelId),0) as RatingScore from Kennel k";

         if(filter.HasWebsite.HasValue && filter.HasWebsite.Value == true)
         {
            sql += " join Contact c on k.kennelid=c.kennelid where c.contacttypeid=2";
         }   
         else
         {
            sql += " where 1=1";
         }
         
         if (filter.KennelStatus.HasValue)
            sql += string.Format(" and k.Status={0}", Convert.ToInt32(filter.KennelStatus.Value));
            
         if (filter.FollowupReason != null)
            sql += string.Format(" and k.followupReasonId={0}", filter.FollowupReason.Id);

         if (filter.State != null)
            sql += string.Format(" and k.State='{0}'", filter.State);

         if (filter.Verified.HasValue)
            sql += string.Format(" and k.IsVerified={0}", Convert.ToInt32(filter.Verified.Value));
         
         IQuery query = DbSessionContext.Current.Session.CreateSQLQuery(sql).AddEntity(typeof(Kennel));

         return query.List<Kennel>();
      }


Please don't laugh at the code too much ;-)

Anyway, just a general update for anyone interested. If you'll note, the big difference is that ReviewCount and RatingScore are always mapped to columns in the result set. I've also specified that they shouldn't be included in any insert or update statements. I'm sure I could clean this up, and I would still be curious on how to resolve the original issue without this workaround, but I'm happy that I can finish up my port from stored procs to NHibernate for this little project.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jan 27, 2008 11:22 pm 
Regular
Regular

Joined: Wed Jun 21, 2006 3:13 pm
Posts: 110
One more update.

Consider the following mapping:

Code:
   <class name="KennelFinder.Rating, KennelFinder" table="Rating">
      <id name="Id" type="Int32">
         <column name="ratingId" not-null="true" unique="true"/>
         <generator class="native" />
      </id>
      
      <property name="Username" column="username" />
      <property name="EmailAddress" column="emailAddress" />
      <property name="EmailThroughSite" column="emailThroughSite" />
      <property name="Score" column="rating" />
      <property name="Text" column="review" />
      <property name="InsertDate" column="insertDate" />
      <property name="Approved" column="approved" />
      <property name="ClientIp" column="clientIp" />

      <many-to-one name="RatedBy" column="accountId" not-null="false" cascade="none" />
      <many-to-one name="Kennel" column="kennelId" not-null="false" cascade="none" />
   </class>


The many-to-one for Kennel does not honor the loader that I had specified in the Kennel mapping. I got around this by just setting a kennelid property and then manually fetching the kennel as needed in Rating as needed.


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