级联还是不级联? (“对象引用了未保存的瞬态实例”错误)

Posted

技术标签:

【中文标题】级联还是不级联? (“对象引用了未保存的瞬态实例”错误)【英文标题】:To cascade or not to cascade? ("object references an unsaved transient instance" error) 【发布时间】:2010-07-16 14:42:29 【问题描述】:

首先,我昨天尝试提出类似的问题 (NHibernate many-to-many relationship question: can select, can't update),但经过一个调试之夜,希望我能以更好的方式(对吗?)重新表述一个问题。如果我做错了什么 - 请告诉 - 我会删除帖子。

下面是描述,后面是一个真正的问题:

给定 USERROLEUSER_ROLE 表,它们分别保持 UsersRoles 和多对多单向关系。目前,User 有一袋 Roles,仅此而已:

用户

  <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(目前很少):

  <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>

角色表

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);

用户表

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 表

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)
);

User 被获取时(我正在跟踪 SQL 查询)——它的关系 AND 角色也被获取:

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

问题来了:当我尝试保存 User 时,NHibernate 会在调试器的深处检查 ​​LastUpdated 的值em>Role 对象并抛出 NHibernate.TransientObjectException对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例),告诉我需要先保存角色。虽然那个角色仍然没有修改。

我尝试在 User 上应用 "save-update""all" 以及其他类型的级联(没有变化获取时)但随后在保存 User 时,它会首先尝试保存 Role 并且我得到一个异常,即违反了角色唯一性约束。

那么,如果合适的话,你能指出我在这里遗漏了什么吗?

更新:不小心修改了Role映射如下(添加unsaved-value="undefined"):

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

查询似乎没问题。 请问这是正确/最佳的解决方案吗

【问题讨论】:

向我们展示您是如何添加新角色的?这就是您在保存用户权限时正在尝试的内容?此外,我在 Role 上没有看到 LastUpdated 属性。你在用户的 RoleList 包上尝试过cascade="all-delete-orphan" 吗? 上面的SQL是我获取用户的时候。修复了最后更新的属性(它在源代码中)。同时我没有添加新角色 - 只是通过 UserId 获取,更新 User Description 并尝试 save User。 cascade="all-delete-orphan" - 尝试过并得到相同的约束违规。 刚刚用某种解决方案/解决方法更新了问题 - 不知道它有多合适。 【参考方案1】:

所以,长话短说(感谢Ayende)版本字段LastUpdated 应该初始化为1,而不是0。然后这一切都在美妙的交响乐中编排。

如此惊人的差异可以在一个位中进行:)

【讨论】:

以上是关于级联还是不级联? (“对象引用了未保存的瞬态实例”错误)的主要内容,如果未能解决你的问题,请参考以下文章

数据未保存:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例 [重复]

org.hibernate.TransientObjectException:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例

MS Access - 级联第二个组合框不级联

7.3

Hibernate中cascade属性的区别

2018.11.4 Hibernate中多对多的关系