【问题标题】:Two many-to-one relationships referencing the same column NHibernate引用同一列 NHibernate 的两个多对一关系
【发布时间】:2020-08-13 16:49:13
【问题描述】:

在过去的半天里一直在四处寻找,结果都干了。本质上,我正在开发一个遗留应用程序,预先存在的 NHibernate 配置将 DB 端上的单个属性/列映射到两个不同的“多对一”关系,因此是两个不同的 C# 属性。 categoriestypes 表共享相同的主键/索引,但在 MySQL 或 NHibernate 中没有明确的关系。

以前这些表是“只读的”,它可以在应用程序中工作,同时避免将其暴露为问题。但是,我最近的任务是允许用户修改和保留对grid 表的更改(为简洁起见,我没有列出其他属性)。

我相信,NHibernate 抛出了一个神秘的异常,它试图将两个单独的“域”级别对象保存到单个列引用。我查看了general_log,更新查询没有到达数据库。注释掉 .hbm.xml 配置文件中的“Cat”行解决了“错误”,但我如何修复映射,使 CatGridType 需要具有相同的 ID 值并且我可以坚持 @ 987654329@ 变回数据库?

public class Grid
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual GridType GridType { get; set; }
    public virtual Cat Cat { get; set; }
    ...
}

数据库架构如下所示(维护现有的怪异设置):

CREATE TABLE `categories` 
  `CatID` smallint(4) NOT NULL,
  `Name` varchar(50) DEFAULT NULL,
  ...
  PRIMARY KEY (`CatID`)

CREATE TABLE `types`
  `TypeID` smallint(4) NOT NULL,
  `Name` varchar(50) DEFAULT NULL,
  ...
  PRIMARY KEY (`TypeID`)

CREATE TABLE `grid` 
  `GridID` smallint(6) NOT NULL AUTO_INCREMENT,
  `Name` varchar(255) DEFAULT NULL,
  `TypeID` smallint(4) NOT NULL,
  ...
  PRIMARY KEY (`GridID`),
  KEY `GridTypeId` (`TypeID`),
  CONSTRAINT `GridTypeId` FOREIGN KEY (`TypeID`) REFERENCES `types` (`TypeID`),

grid 通过 MySQL 中的外键引用表 types,但在 DB 端没有任何东西将它与 categories 联系起来。 NHibernate 配置目前是这样设置的,唯一的例外是之前的“网格”配置之前是 mutable="false"cache usage="read-only"

  <class name="Grid, Domain" table="grid" lazy="true">
    <cache usage="read-write" />
    <id name="Id" column="GridID" unsaved-value="0">
      <generator class="identity" />
    </id>
    ...
    <many-to-one name="GridType" column="TypeID" fetch="join" not-null="true" />
    <many-to-one name="Cat" column="TypeID" not-null="true" />
  </class>

  <class name="Cat, Domain" table="categories" lazy="true" mutable="false">
    <cache usage="read-only"/>
    <id name="Id" column="CatID" unsaved-value="0">
      <generator class="identity" />
    </id>
    <property name="Name" />
    ...
  </class>

  <class name="GridType, Domain" table="types" mutable="false">
    <cache usage="read-only"/>
    <id name="Id" column="TypeID"/>
    <property name="Name" />
    ...
  </class>

将尝试说服我的老板这值得重组,因为整个数据库区域都是一团糟,但是这一个遗留应用程序;所以我想知道我的选择取决于我们未来发展的可用性。


编辑:试用 Radim Köhler 下面的建议。唯一的问题是(a)有人可以切换Cat 的值并错误地认为他们已经对数据库进行了更改,但它不会被持久化。 (b) 如果有人更新了GridType,它会更新数据库而不是缓存; Cat 将保持旧值,直到 NHibernate 再次从数据库中撤出(即 CatGridType 的 ID 将被“取消同步”,这是我的问题的一部分)。

我正在尝试的修复是“当设置了 Grid 上的 GridType / Cat 值时,通过从缓存中获取适当的 ID 来设置另一个”。还将阻止某人设置另一个表中可能不存在的 gridtype / cat。

public class Grid {

    ...

    private GridType _gridType;
    private Cat _cat;

    public virtual GridType GridType
    {
        get => _gridType;
        set
        {
            var cat = _catRepository.GetById(value.Id);
            _cat = cat ?? throw new InvalidOperationException("GridType is not valid Cat");
            _gridType = value;
        }
    }

然后对public virtual Cat Cat 重复上述操作。如果有人对此有任何疑问,请随时发表评论。

【问题讨论】:

    标签: c# mysql nhibernate


    【解决方案1】:

    我们可以在映射中将一列用于多种用途...但只有一个属性可以编辑...所有其他属性必须是只读的。我们可以使用insert="false" update="false"

    这适用于我们的情况:

    <many-to-one name="GridType" column="TypeID" ... />
    <many-to-one name="Cat"      column="TypeID" insert="false" update="false" />
    

    【讨论】:

    • 我想过这个问题,它几乎解决了我的问题。唯一的问题似乎是缓存问题,如果我更新 GridType 而不是 Cat 并提交事务,那么列会在 DB 端正确更新,GridType 是正确的,但 Cat 仍然是在刷新缓存之前相同。我想我可以在应用程序中以编程方式更新Cat 每当GridType 被更改/专门为该属性摆弄缓存并使其工作。感谢您的建议。
    猜你喜欢
    • 2013-01-18
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2013-03-08
    相关资源
    最近更新 更多