nH performing UPDATE for new entities; stalestateexception happens

36 views
Skip to first unread message

c j

unread,
May 30, 2019, 7:17:00 AM5/30/19
to nhusers
Hi all. I have a problem that nh is performing an UPDATE for 3 entities I've just created, and getting a batch update failed, actual: 0, expected 3. I can see why, because these entities don't exist, so there is nothing to update, but I'm curious as to the mechanism that the uow is coming to decide that they're existing. In debug mode my code will output all sqls ever run by nH to the console, and there is definitely never an INSERT for these entities

The problem only appeared after upgrading the nuget package from 2.x to 5.2

In terms of the data structure in the db, a project has many projectgroups and a project has many projectparticipants. Many projectparticipants have many projectgroups and this m:m relationship is decomposed by a projectgroupparticipants table

I create 2 ProjectParticipants and add them to the Project.Participants collection. Then I populate the Project.Groups collection with 3 new groups. Finally I cross reference them by adding the participants to the relevant groupX.Participants, and adding the group to participantX,Groups. (Group A has participant 1, Group B has participant 2, Group C has participants 1 and 2)
Upon saving the changes I encounter a "StaleStateException - Batch update returned unexpected row count from update; actual row count: 0, expected: 3" in relation to creating the 3 groups. I would expect nHibernate to INSERT the new groups but it's running an UPDATE query and balking that they don't exist. It doesn't get as far as assigning the the participants to the groups

Here are the mappings:

//ProjectMap: A Project..
Id(x => x.Id).GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
HasMany(x => x.Participants)
.Table("ProjectParticipants")
.KeyColumn("ProjectId")
.ApplyFilter(DeletedDateFilter.FilterName)
.Cascade.AllDeleteOrphan()
.Inverse();

HasMany(x => x.Groups)
.Table("ProjectGroups")
.KeyColumn("ProjectId")
.Cascade.AllDeleteOrphan()
.Inverse();


//ProjectParticipantMap: A ProjectParticipant…
Id(x => x.Id).GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
References(x => x.Project)
.Column("ProjectId")
.LazyLoad(Laziness.Proxy);
HasManyToMany(x => x.Groups)
.Table("ProjectGroupParticipants")
.ParentKeyColumn("ProjectParticipantId")
.ChildKeyColumn("ProjectGroupId");


//GroupMap: A Group...
Id(e => e.Id).GeneratedBy.Assigned().UnsavedValue(Guid.Empty);
References(e => e.Project)
.Column("ProjectId")
.LazyLoad(Laziness.Proxy);
HasManyToMany(x => x.Participants)
.Table("ProjectGroupParticipants")
.ParentKeyColumn("ProjectGroupId")
.ChildKeyColumn("ProjectParticipantId")
.ApplyChildFilter(DeletedDateFilter.FilterName);


The tables are (hopefully doesn't get messed up):

[ProjectParticipants] 1-->M [ProjectGroupParticipants] M<--1 [ProjectGroups]
M M
\---------------->1 [Project] 1<--------------------/


Here are the SQLs being run by nHibernate:

--presume this is adding the first participant - I find him in the db
INSERT INTO ProjectParticipants (CreatedDate, ModifiedDate, DeletedDate, FirstName, LastName, Timezone, Email, Pseudonym, Role, ProjectId, UserId, MobileNumber, Id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

--presume this is adding the second participant - I find her in the DB
INSERT INTO ProjectParticipants (CreatedDate, ModifiedDate, DeletedDate, FirstName, LastName, Timezone, Email, Pseudonym, Role, ProjectId, UserId, MobileNumber, Id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

--not sure what this is doing
UPDATE Projects SET CreatedDate = ?, ModifiedDate = ?, LogoUrl = ?, LogoFilename = ?, Client = ?, Name = ?, Description = ?, LastAccessedDate = ? WHERE Id = ?

--not sure what this operation is for, but at this point in time NO GROUP EXISTs for this project ID
SELECT … FROM ProjectGroups groups0_ WHERE groups0_.ProjectId=?

--not sure what this is for either?
UPDATE Projects SET CreatedDate = ?, ModifiedDate = ?, LogoUrl = ?, LogoFilename = ?, Client = ?, Name = ?, Description = ?, LastAccessedDate = ? WHERE Id = ?

-- I've no idea why this is an UPDATE instead of an INSERT, but it will certainly update 0 rows instead of X, because no groups exist
UPDATE ProjectGroups SET CreatedDate = ?, ModifiedDate = ?, DeletedDate = ?, Name = ?, ProjectId = ? WHERE Id = ?
Exception thrown: 'NHibernate.StaleStateException' in NHibernate.dll
Batch update returned unexpected row count from update; actual row count: 0; expected: 3
[ UPDATE ProjectGroups SET CreatedDate = @p0, ModifiedDate = @p1, DeletedDate = @p2, Name = @p3, ProjectId = @p4 WHERE Id = @p5 ]

So why did nHibernate come to think that its local entity had already ben saved and was hence available to UPDATE? The SQL generated should be an insert, but I'm not sure how it manages sync between local cache and DB to know if entities already exist or not

Slightly puzzled, that this used to work in NH 2.x, but since an upgrade to latest (5.x) this exception has started appearing.

If any of the code/ascii art diagram above is messed up, there is a markdown formatted version of this question on SO at https://stackoverflow.com/questions/56365708/nhibernate-is-trying-to-sql-update-an-entity-i-just-created-rather-than-insert

Many thanks for any pointers

c j

unread,
May 30, 2019, 2:50:48 PM5/30/19
to nhusers
Chased the cause of this with advice from SO - declaring the Groups Id as Assigned,UnsavedValueGuidEmpty was the fault. We were setting non empty guid upon create so nh was assuming it existed. Changed the UnsavedValue to "undefined" to force a db query first to determine INSERT or UPDATE.

Prev code worked due to a bug in nH, fixed in 5.2
Reply all
Reply to author
Forward
0 new messages