I have a many-to-many join relationship which functions as I want in all cases, except that if I delete the record on inverse side of the relationship it does not automatically delete the join record, and it leaves bad data in my database. In my case I have a table for User and a table for Role, with a UserRole join table between them. I don't want to cascade deletes, because deleting a user shouldn't delete a role, or vice-versa. However, deleting a user OR a role should delete the UserRole record connecting them.
The relationships between the classes are defined as:
Code:
<class name="SysUser" table="USysUser" lazy="true" optimistic-lock="version" dynamic-update="true" dynamic-insert="true" >
<cache usage="read-write" />
<id name="Guid" column="[UserGUID]" type="Guid">
<generator class="assigned" />
</id>
<property column="[UserName]" type="String" name="Name" not-null="true" length="256" />
<idbag name="ListOfSysRole" lazy="true" inverse="true" collection-type="SysRolePersistentIdListType" table="USysUserRole" cascade="save-update">
<collection-id column="UserRoleGUID" type="Guid">
<generator class="guid" />
</collection-id>
<key column="UserGUID"/>
<many-to-many class="SysRole" column="RoleGUID" />
</idbag>
</class>
<class name="SysRole" table="USysRole" lazy="true" optimistic-lock="version" dynamic-update="true" dynamic-insert="true" >
<cache usage="read-write" />
<id name="Guid" column="[RoleGUID]" type="Guid">
<generator class="assigned" />
</id>
<property column="[RoleName]" type="String" name="Name" not-null="true" length="50" />
<idbag name="ListOfSysUser" lazy="true" inverse="false" collection-type="SysUserPersistentIdListType" table="USysUserRole" cascade="save-update">
<collection-id column="UserRoleGUID" type="Guid">
<generator class="guid" />
</collection-id>
<key column="RoleGUID"/>
<many-to-many class="SysUser" column="UserGUID" />
</idbag>
</class>
I can create a new role and associate existing users and the join gets inserted properly when I save the role. I can create a new user and associate to an existing role and the join gets inserted properly when I save the user. I can remove relationships and they're properly removed from the join table (I have to remove from the collection property on both the user and the role). And, finally, if I delete a role then it properly deletes the join record from USysUserRole, but leaves the associated user record alone, since I'm using save-update, not delete.
The only scenario that isn't working is when I delete a user record it doesn't automatically delete the USysUserRole join record. This is because inverse="true" for the user declaration, because if I reverse this and put inverse="false" for user and "true" for role then the problem gets reversed, and then it doesn't delete the join when I delete a role.
I can force it to delete the join record(s) by removing all roles from the user's ListOfSysRole property and the roles' ListOfSysUser and then calling delete on the user, but this seems wrong. It really feels like Hibernate should know that if I'm deleting an object on one side of a join, regardless of the "inverse" setting, then the references in the join table must also be deleted. If this doesn't work then I need to ensure that all references to an object are removed before removing that object, otherwise I'll end up with "hanging" references in my database.
I've tried setting inverse="false" on both sides and then I get loads of problems as both sides try to insert join records, and I get duplicates. I've tried inverse="true" on both sides and then I never get join records at all. I've tried different cascade settings with no success. Basically I'm out of ideas.
Does anyone know whether I'm just expecting NHibernate to do too much for me here? Is this something where NHibernate has to be explicitly told to remove the relationship from the non-inverse side?