2

我在照片和标签之间有一个多对多的关系:一张照片可以有多个标签,而几张照片可以共享相同的标签。

我有一个循环扫描目录中的照片,然后将它们添加到 NHibernate。在此过程中,一些标签会添加到照片中,例如,当照片拍摄于 2009 年时,会添加 2009 年标签。

Tag 类实现 Equals 和 GetHashCode 并使用 Name 属性作为唯一的签名属性。Photo 和 Tag 都有代理键并且是版本化的。

我有一些类似于以下的代码:

public void Import() {
    ...
    foreach (var fileName in fileNames) {
        var photo = new Photo { FileName = fileName };
        AddDefaultTags(_session, photo, fileName);
        _session.Save(photo);
    }
    ...
}

private void AddDefaultTags(…) {
    ...
    var tag =_session.CreateCriteria(typeof(Tag))
                    .Add(Restriction.Eq(“Name”, year.ToString()))
                    .UniqueResult<Tag>();

    if (tag != null) {
        photo.AddTag(tag);
    } else {
        var tag = new Tag { Name = year.ToString()) };
        _session.Save(tag);
        photo.AddTag(tag);
    }
}

我的问题是标签不存在时,例如新年的第一张照片。AddDefaultTags 方法检查标签是否存在于数据库中,然后创建它并将其添加到 NHibernate。添加单张照片时效果很好,但是在新的一年和同一工作单元中导入多张照片时它会失败,因为它仍然不存在于数据库中并再次添加。当完成工作单元时,它会失败,因为它试图在标签表中添加两个同名的条目......

我的问题是如何确保 NHibernate 在上述情况下只尝试在数据库中创建单个标签。我是否需要自己维护一个新添加标签的列表,或者我可以设置映射以使其工作?

4

3 回答 3

2

_session.Flush()如果您的条件不应返回陈旧数据,则需要运行。或者您应该能够通过将 设置_session.FlushMode为 Auto 来正确执行此操作。

使用 FlushMode.Auto,会话将在条件执行之前自动刷新。

编辑:很重要!阅读您显示的代码时,您看起来不像是在为您的工作单元使用事务。我建议将您的工作单元包装在事务中 - 如果您使用的是 NH2.0+,则 FlushMode.Auto 需要这样做!

在此处进一步阅读:NHibernate ISession Flush:何时何地使用它,为什么?

于 2009-05-03T19:32:49.980 回答
0

如果您希望新标签在每次检查时都在数据库中,您需要在保存后提交事务以将其放在那里。

另一种方法是在处理照片之前将标签读入集合。然后就像您说的那样,您将在本地搜索并根据需要添加新标签。完成文件夹后,您可以提交会话。

您应该发布您的映射,因为我可能没有正确解释您的问题。

于 2009-05-03T19:25:14.507 回答
0

这是典型的“锁定不存在的东西”问题。我已经多次面对它,但仍然没有一个简单的解决方案。

这是我到目前为止所知道的选项:

  • 乐观:对名称有一个唯一的约束,并让其中一个会话在提交时抛出。然后你再试一次。您必须确保在发生另一个错误时不会以无限循环结束。
  • 悲观:当你添加一个新的 Tag 时,你会使用 TSQL 锁定整个 Tag 表。
  • .NET 锁定:您使用 .NET 锁同步线程。这仅在您并行事务处于同一进程中时才有效。
  • 使用自己的会话创建标签(见下文)

例子:

public static Tag CreateTag(string name)
{
  try
  {
    using (ISession session = factors.CreateSession())
    {
      session.BeginTransaction();
      Tag existingTag = session.CreateCriteria(typeof(Tag)) /* .... */
      if (existingtag != null) return existingTag;
      {
        session.Save(new Tag(name));
      }
      session.Transaction.Commit();
    }
  }
  // catch the unique constraint exception you get
  catch (WhatEverException ex)
  {
    // try again
    return CreateTag(name);
  }
}

这看起来很简单,但有一些问题。你总是得到一个标签,要么是现有的,要么是创建的(并立即提交)。但是您获得的标签来自另一个会话,因此它与您的主会话分离。您需要使用级联(您可能不希望这样做)或更新将其附加到您的会话中。

创建标签不再与您的主事务耦合,这是目标,但也意味着回滚您的事务会将所有创建的标签留在数据库中。换句话说:创建标签不再是您交易的一部分。

于 2009-05-04T08:49:04.013 回答