-->
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.  [ 15 posts ] 
Author Message
 Post subject: Collection element removed and re-added inserts duplicate
PostPosted: Wed Sep 13, 2006 10:40 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
I have an object that holds a collection of other objects. The model is like this:

A User has many Roles.

So in my UI I have a checkbox list of roles and when you select the user it checks the roles that the user already has. To remove the role you uncheck the box. To add the role you check the box. Simple enough... but I have a problem:

If the user unchecks a role, then checks it again and saves ... it tries to insert another row into the table, because the collection was modified. This of course throws a duplicate primary key error and the operation fails.

I understand that the collection is modified, but is logically equivalent to it's previous value, so no inserts should be made. How can I express this in NHibernate?

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 12:54 am 
Regular
Regular

Joined: Tue Feb 21, 2006 9:50 am
Posts: 107
we do deletions in collections explicitly before saving. In your case we would call a session.delete() if the user unchecks a role.

Regards
Klaus


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 4:20 am 
Beginner
Beginner

Joined: Wed Aug 03, 2005 8:06 am
Posts: 40
Location: Netherlands
In a case similar to yours we do user.Roles.Clear(), in your terminology, and then re-add the checked roles. For small collections this is no big deal.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 5:30 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Can you post your mappings and code? NHibernate should actually be smart enough to detect that nothing has changed, or to delete the old object first and insert the new one.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 11:28 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
Here's the relevant code for the user's role management:

Code:
Public Sub RemoveRole(ByRef role As Role)
        If Me.RoleAssignments(role) IsNot Nothing Then
            _roleAssignments.Remove(RoleAssignments(role))
        End If
    End Sub

Public Sub AssignRole(ByVal role As IRole, ByVal ParamArray constraints As IConstraint())
        If Not Me.Applications.Contains(role.Application) Then
            Throw New InvalidOperationException(String.Format("The user {0} does not have access to role's application ({1})", Me.LoginId, role.Application.ApplicationId))
        End If

        'if the user already has this role, append the constraints
        If HasRole(role, Context.Emtpy) Then
            For Each constraint As CompositeConstraint In constraints
                Me.RoleAssignments(role).AddConstraint(constraint)
            Next
        Else
            'user doesn't have role, add a new role assigment
            Me._roleAssignments.Add(RoleAssignment.Create(Me, role, constraints))
        End If


    End Sub




...and here is the mapping file for User:

Code:
<class name="User" table="[User]">
    <id name="LoginId" column="LoginId" type="GSF.Uaas.DataAccess.CaseInsensitiveStringType, GSF.Uaas.DataAccess" length="50" access="nosetter.camelcase-underscore">
      <generator class="assigned" />     
    </id>

    <!-- main properties -->
    <property name="FirstName" column="firstName" type="String" length="50" />
    <property name="LastName" column="lastName" type="String" length="50" />
   
    <!-- associations -->
    <bag name="roleAssignments" lazy="true" inverse="true" access="NHibernate.Generics.GenericAccessor, NHibernate.Generics" cascade="all-delete-orphan">
      <key column="LoginId" />
      <one-to-many class="RoleAssignment"/>
    </bag>

    <set name="applications" lazy="true" access="NHibernate.Generics.GenericAccessor, NHibernate.Generics" table="AppUserXRef">
      <key column="LoginId" />
      <many-to-many class="Application" column="ApplicationId" />
    </set>
 
  </class>


...I hope this is readable. This is a legacy (read: crap) db schema and is proving very difficult to bolt on NHibernate to work with it.

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 2:46 pm 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
luedi wrote:
we do deletions in collections explicitly before saving. In your case we would call a session.delete() if the user unchecks a role.

Regards
Klaus


I'm not deleting an object though, only it's association. If I have a User and a Role, I don't delete anything other than the association between U and R.

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 3:21 pm 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
I wonder if NHibernate.Generics causes this somehow. Have you tried running without it? Also, is RoleAssignments a bag or a map? I'm a bit confused.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 14, 2006 4:04 pm 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
RoleAssignments is a bag... should it be a map? It's been a while since I wrote that and don't remember my exact reasons for it.

Nhibernate.Generics might be the culprit, but I'm not sure. Running without it might make me have to change a lot of code, but I'll look into it.

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 15, 2006 4:20 am 
Newbie

Joined: Thu Apr 27, 2006 6:03 am
Posts: 14
Why do you start a server roundtrip every time a checkbox is checked or unchecked? Just iterate your checkboxlist when the user clicks the save button and set or delete the references at this time. That will avoid unnecessary server roundtrips and solve your problem.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 15, 2006 8:45 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
RobRoma wrote:
Why do you start a server roundtrip every time a checkbox is checked or unchecked? Just iterate your checkboxlist when the user clicks the save button and set or delete the references at this time. That will avoid unnecessary server roundtrips and solve your problem.


I'm not doing a server-round trip here.

I have an object with a collection. I modify the collection in a unit of work and then the user clicks SAVE. My problem is that the collection is issuing unnecessary inserts when nothing really changed. This causes a duplicate key violation in the database.

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 15, 2006 9:47 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
POST REMOVED: I posted it to the wrong thread. Sorry[/b]

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Last edited by subdigital on Fri Sep 15, 2006 10:55 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 15, 2006 10:49 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Looks like you pasted a wrong test.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 15, 2006 10:53 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
oops! you're right! I have 2 threads going on right now. grrr. I'll repost it to the correct thread and edit it out.

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 20, 2006 10:12 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
UPDATE: I changed the logging to go to the database and wrote a simple viewer so I could have more visibility over what's going on here.


I stepped through the code and it works as expected:
Uncheck:
-removes Role from RoleAssigments collection

Check:
-adds new Role to RoleAssignments collection


when I hit save NHibernate tries to insert first, which throws a PRIMARY KEY VIOLATION in the database.

Here's the logs (snipped for clarity):

Quote:
...snipped irrelevant startup logs....

- Binding roles for user: dummy.user
- Removing role ROLE_ABC from user dummy.user
- Assigning role ROLE_ABC Viewer to user dummy.user

SELECT applicatio0_.LoginId as LoginId__, applicatio0_.ApplicationId as Applicat2___ FROM MYDATABASE.dbo.AppUserXRef applicatio0_ WHERE applicatio0_.LoginId=@p0

-Saving changes...
-Saving entity: APP (Test.Application)

-Checking whether to save or update entity: Test.RoleAssignment (Test.RoleAssignment)...

- Saving entity: APP (Test.Application)
- Entity Test.RoleAssignment is new. Saving...

-ERROR! System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_UserRole'. Cannot insert duplicate key in object 'UserRole'. The statement has been terminated. [stack trace snipped]
[/list]

Any ideas?

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 20, 2006 11:49 am 
Regular
Regular

Joined: Tue May 31, 2005 3:18 pm
Posts: 117
Location: Houston
Here's some more verbose logging statments about the Unit of Work in Question:


Quote:
-SaveOrUpdate() unsaved instance
-NHibernate.Impl.SessionImpl Saving entity: APP (Test.Application) generated identifier: 8d1523e8-a481-45f9-b34c-984200a0cf91

This line is saving the new role
-saving [Test.RoleAssignment#8d1523e8-a481-45f9-b34c-984200a0cf91]

-processing cascades for: Test.RoleAssignment
-done processing cascades for: Test.RoleAssignment

-Wrapped collection in role: Test.RoleAssignment.constraints
-processing cascades for: Test.RoleAssignment

-cascading to collection: Test.RoleAssignment.constraints
-done processing cascades for: Test.RoleAssignment

This line is deleting the previous (same) role
-deleting a persistent instance
-deleting [Test.RoleAssignment#88ff51c8-f7f0-4aef-b010-efb06d11e939]
-processing cascades for: Test.RoleAssignment
-done processing cascades for: Test.RoleAssignment

_________________
------------------------------
Ben Scheirman
http://www.flux88.com


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