views:

72

answers:

1

Hello, gentlemen! First, I've tried to ask the similar question yesterday (http://stackoverflow.com/questions/3255407/nhibernate-many-to-many-relationship-question-can-select-cant-update), but after a debugging night hopefully I can rephrase a question the better (right?) way. If I am doing something wrong by that - please tell - I'll erase the post.

Here comes the description followed by a real question:

Given USER, ROLE and USER_ROLE tables which keep Users, Roles and the many-to-many unidirectional relation respectively. Currently, User has a bag of Roles and that's it:

USER:

  <class name="User" lazy="false" table="Users">
    <id name="Id" type="int">
      <generator class="native"/>
    </id>
    <version name="m_lastChanged" access="field" column="LastChanged" generated="never" type="int"/>

    <property name="Name" />

    <bag name="RoleList" table="User_Role" lazy="false" collection-type="Roles">
      <key column="UserId" foreign-key="Id"/>
      <many-to-many class="Role" column="RoleId"/>
    </bag>

  </class>

ROLE (there are currently very very few of them):

  <class name="Role" lazy="false" table="Roles">
    <id name="Id" type="int">
      <generator class="native"/>
    </id>
    <version name="m_lastChanged" access="field" column="LastChanged" generated="never" type="int"/>

    <property name="Name" />
    <property name="Description" />
  </class>

ROLE TABLE:

CREATE TABLE [Roles] (
[Id] INTEGER NOT NULL PRIMARY KEY,
[Name] text  NOT NULL,
[LastChanged] INT NOT NULL DEFAULT(0)
);

CREATE UNIQUE INDEX uidxUserName ON Roles (Name COLLATE NOCASE);

USER TABLE:

CREATE TABLE [Users] (
[Id] INTEGER NOT NULL PRIMARY KEY,
[Name] text NOT NULL,
[LastChanged] INT NOT NULL DEFAULT(0)
);

CREATE UNIQUE INDEX uidxRoleName ON Users (Name COLLATE NOCASE);

USER_ROLE TABLE:

CREATE TABLE [User_Role] (
[UserId] INTEGER NOT NULL,
[RoleId] INTEGER NOT NULL,
[LastChanged] INT NOT NULL DEFAULT(0),
PRIMARY KEY (UserId, RoleId),
FOREIGN KEY (UserId) REFERENCES Users(Id),
FOREIGN KEY (RoleId) REFERENCES Roles(Id)
);

When User is fetched (I am tracking the SQL queries) – its relations AND roles are fetched as well:

SELECT
    rolelist0_.UserId as UserId1_,
    rolelist0_.RoleId as RoleId1_,
    role1_.Id as Id2_0_,
    role1_.LastChanged as LastChan2_2_0_,
    role1_.Name as Name2_0_,
    role1_.Description as Descript4_2_0_
FROM
    User_Role rolelist0_
left outer join
    Roles role1_
        on rolelist0_.RoleId=role1_.Id
WHERE
    rolelist0_.UserId=@p0;
@p0 = 2

SELECT
    user0_.Id as Id3_0_,
    user0_.LastChanged as LastChan2_3_0_,
    user0_.Name as Name3_0_,
    user0_.Password as Password3_0_,
    user0_.FullName as FullName3_0_,
    user0_.EMail as EMail3_0_,
    user0_.PhoneNumber as PhoneNum7_3_0_,
    user0_.Description as Descript8_3_0_
FROM
    Users user0_
WHERE
    user0_.Id=@p0;
@p0 = 2

And here comes the problem: when I am trying to save User then deep in the debugger the NHibernate checks the LastUpdated value of Role object and throws NHibernate.TransientObjectException (object references an unsaved transient instance - save the transient instance before flushing), telling that I need to save Role first. While that the role is still unmodified.

I've tried to apply the "save-update" and "all" and other types of cascaded on User (no changes while fetching) but then when saving User it tries to save Role first and I am getting an exception that the role uniqueness constraint is violated.

So, if appropriate, can you point what am I missing here, please?

Update: By accident modified the Role mapping in the following way (added unsaved-value="undefined"):

<version name="m_lastChanged" access="field" column="LastChanged" unsaved-value="undefined" generated="never" type="int"/>

The queries seem to be alright. Is it the correct/best solution, please?

A: 

So, to make the long story short (and thanks to Ayende) the version field LastUpdated should be initialized to 1, not to 0. Then it all orchestrates in a lovely symphony.

So incredible how much difference can be carried in a single bit :)

BreakPhreak