-->
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.  [ 9 posts ] 
Author Message
 Post subject: SaveOrUpdate not incrementing identity column correctly?
PostPosted: Wed Jul 25, 2007 2:48 pm 
Newbie

Joined: Mon Aug 30, 2004 6:49 pm
Posts: 7
Location: Fort Washington, PA
Hello...I have used Hibernate for a long time now and I am just starting to experiment with NHibernate on a new project. I am running into an issue that I never encountered with Hibernate. I've searched for the better part of two days and still haven't found a solution. Any help would be greatly appreciated.

The project I am on uses a SQL Server 2005 database. The data model is very large and complex and makes heavy usage of triggers throughout in order to store historical data, etc. Because of the trigger usage, I want to return a refreshed version of each object following a save or update to get any changes that may have resulted from a trigger firing. To facilitate this, I have created the following method in a generic DAO class:

/// <summary>
/// Inserts or updates the object to the associated datastore and then refreshes the object,
/// returning the refreshed object. This is applicable for situations where triggers are
/// utilized, so that any changes resulting from the triggers will be reflected in the returned
/// object instance.
/// </summary>
/// <param name="entity">
/// The object to be saved or updated.
/// </param>
/// <returns>
/// The object following the save.
/// </returns>
public T SaveOrUpdateAndRefresh(T entity)
{
return this.HibernateTemplate.Execute<T>(
delegate(ISession session)
{
session.SaveOrUpdate(entity);
session.Flush();
session.Refresh(entity);
return entity;
});
}

As you can see, this method calls SaveOrUpdate followed by a Flush and Refresh prior to returning the object.

To test this out, I created a simple domain object. The condensed Hibernate mapping file follows:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Test.Model"
assembly="Test.Core"
schema="dbo">

<class name="Asset" table="tblAsset" lazy="true">

<cache usage="read-write"/>

<id name="Uid" column="UID" type="Int32" unsaved-value="0">
<generator class="identity"/>
</id>

<property name="AssetName" column="AssetName" type="string"/>

</class>

</hibernate-mapping>

Then I created a couple of integration tests using the Spring framework. One method tests the update of an existing object in the database, while another tests the creation of a brand new object:

[Test]
public void TestSaveAsset()
{
Asset asset = AssetService.GetAsset(31);
Assert.IsNotNull(asset);
Assert.AreEqual("4608 Braxton Drive", asset.AssetName);

asset.AssetName = "4608 Braxton Drive - Changed";
asset = AssetService.SaveAsset(asset);

Assert.AreEqual("4608 Braxton Drive - Changed", asset.AssetName);
}

[Test]
public void TestSaveNewAsset()
{
this.SetComplete();

Asset asset = new Asset();
asset.AssetName = "TEST INSERTING ASSET";
asset.AssetTypeUid = 1;
asset.BaseCurrencyUid = 1;

asset = AssetService.SaveAsset(asset);

Assert.AreEqual("TEST INSERTING ASSET", asset.AssetName);
}


The AssetService class, which is not listed, simply calls the associated DAO class's SaveOrUpdateAndRefresh method when its SaveAsset method is called.

The first method, TestSaveAsset works just fine with the asset being updated and refreshed.

The second method, TestSaveNewAsset, actually saves the new instance to the database with the UID identity column properly incremented. However, using a couple of debug statements, I found that the Uid property before the call to SaveOrUpdateAndRefresh is set at the appropriate unsaved-value of 0. However, following the call to SaveOrUpdate, the value is set to 1. This is always the case regardless of the unsaved-value. Therefore, when a refresh is attempted, the following error is thrown:

Test.Service.IAssetServiceIntegrationTest.TestSaveNewAsset : Spring.Data.NHibernate.HibernateObjectRetrievalFailureException : No row with the given identifier exists: 1, of class: Test.Model.Asset
----> NHibernate.UnresolvableObjectException : No row with the given identifier exists: 1, of class: Test.Model.Asset

Has anyone seen anything similar to this before? It seems as though the SaveOrUpdate call is always setting the Uid column to 1 regardless of what the associated value is actually set to in the database during the insert.

Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 3:09 pm 
Hibernate Team
Hibernate Team

Joined: Tue Jun 13, 2006 11:29 pm
Posts: 315
Location: Calgary, Alberta, Canada
My experience with Spring.NET is that sometimes it hides the true error thrown from NHibernate.

Check that you indeed have an AssetType of ID 1 and BaseCurrency of ID 1.

_________________
Karl Chu


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 3:21 pm 
Newbie

Joined: Mon Aug 30, 2004 6:49 pm
Posts: 7
Location: Fort Washington, PA
karlchu wrote:
My experience with Spring.NET is that sometimes it hides the true error thrown from NHibernate.

Check that you indeed have an AssetType of ID 1 and BaseCurrency of ID 1.


I do...sorry, I didn't include those values in my condensed mapping file, but they are there and do exist in the database. The values are inserted along with a new tblAsset row during my integration test, so I don't think that has anything to do with it.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 4:25 pm 
Hibernate Team
Hibernate Team

Joined: Tue Jun 13, 2006 11:29 pm
Posts: 315
Location: Calgary, Alberta, Canada
I guess the only suggestion I would have at this point is to dig all the way into the inner exceptions. I did have some issues with Spring.NET burying the NHibernate exception. Sorry that I wasn't of much help.

_________________
Karl Chu


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 4:37 pm 
Newbie

Joined: Mon Aug 30, 2004 6:49 pm
Posts: 7
Location: Fort Washington, PA
karlchu wrote:
I guess the only suggestion I would have at this point is to dig all the way into the inner exceptions. I did have some issues with Spring.NET burying the NHibernate exception. Sorry that I wasn't of much help.


No problem...I'm not really worried about the exception that is being thrown because the UnresolvableObjectException that is being thrown makes complete sense...it is thrown because before the call to SaveOrUpdate, the Asset that was passed has a Uid value of 0, which matches the unsaved-value in the mapping file. The SaveOrUpdate method inserts a record into the database without a problem and the UID value in the database is incremented correctly. For instance, the last time I ran the test, it inserted a row with a UID value of 713. However, after the call to SaveOrUpdate, the object's Uid value is set to 1. So, when the refresh method is called, it is looking for a row with a UID value of 1, which it does not find...that's why the Exception is thrown. I'm just wondering why the SaveOrUpdate call doesn't update the Asset's Uid value to 713. Why does it always set it to 1?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 4:45 pm 
Hibernate Team
Hibernate Team

Joined: Tue Jun 13, 2006 11:29 pm
Posts: 315
Location: Calgary, Alberta, Canada
Ahhhh... (light bulb)... do your triggers return anything? I think you need this in the trigger:
Code:
SET NOCOUNT ON

The "1" you are getting is probably from the trigger, not the insert statement.

_________________
Karl Chu


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 4:52 pm 
Newbie

Joined: Mon Aug 30, 2004 6:49 pm
Posts: 7
Location: Fort Washington, PA
karlchu wrote:
Ahhhh... (light bulb)... do your triggers return anything? I think you need this in the trigger:
Code:
SET NOCOUNT ON

The "1" you are getting is probably from the trigger, not the insert statement.


They all have SET NOCOUNT ON. I'm going to write a quick test on a table with no triggers whatsoever to see if the same thing happens.

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 9:25 am 
Newbie

Joined: Mon Aug 30, 2004 6:49 pm
Posts: 7
Location: Fort Washington, PA
You were right!!! Thanks!!! The table to which I was trying to insert has four triggers on it. Each one has SET NOCOUNT ON specified, however, one of the triggers is still returning a 1 for some reason. I'll dig into it more, but that seems to be the root of the problem...make sure any triggers have SET NOCOUNT ON specified, otherwise, calls to SaveOrUpdate could refresh the associated object's identity column with something other than the actual identity value created by the database.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 28, 2007 6:05 pm 
Hibernate Team
Hibernate Team

Joined: Tue Jun 13, 2006 11:29 pm
Posts: 315
Location: Calgary, Alberta, Canada
Glad your problem is solved. :-)

_________________
Karl Chu


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