多对多在 NHibernate 中删除两个具有共同关联的父级

发布于 2024-10-09 10:49:17 字数 4528 浏览 0 评论 0原文

我的应用程序中有 3 个顶级实体:电路、问题、文档

电路可以包含文档,问题可以包含文档。

当我删除电路时,我希望它删除与其关联的文档,除非它被其他东西使用。我希望问题也能有同样的行为。当唯一的关联位于数据库中的同一个表中时,我可以让它工作,但如果它位于另一个表中,那么它会由于外键约束而失败。

ex 1(这将正确级联,因为从电路到文档仅存在外部约束
Document1 存在。
Circuit1 存在并包含对 Document1 的引用。
如果我删除 Circuit1,那么它也会删除 Document1。

ex 2(这将正确级联,因为从电路到文档仅存在外部约束。
Document1 存在。
Circuit1 存在并包含对 Document1 的引用。
Circuit2 存在并包含对 Document1 的引用。
如果我删除 Circuit1,则它会被删除,但 Document1 不会被删除,因为 Circuit2 存在。
如果我随后删除 Circuit2,则 Document1 也会被删除。

ex 3(这将引发错误,因为当它删除电路时,它发现没有其他电路引用该文档,因此它尝试删除该文档。但是它不应该,因为存在一个问题对文档的外部约束。)
文档 1 已存在。
Circuit1 存在并包含对 Document1 的引用。
Issue1 存在并包含对 Document1 的引用。
如果我删除 Circuit1,则会失败,因为它尝试删除 Document1,但 Issues1 仍然有引用。

数据库:
这个想法不允许上传图像,所以这里是数据库的ERD: http://lh3.ggpht.com/_jZWhe7NXay8/TROJhOd7qlI/AAAAAAAAAGU/rkni3oEANvc/CircuitIssues.gif

型号:

public class Circuit
{
    public virtual int CircuitID { get; set; }
    public virtual string CJON { get; set; }
    public virtual IList<Document> Documents { get; set; }
}
public class Issue
{
    public virtual int IssueID { get; set; }
    public virtual string Summary { get; set; }
    public virtual IList<Model.Document> Documents { get; set; }
}
public class Document
{
    public virtual int DocumentID { get; set; }
    public virtual string Data { get; set; }
}

映射文件:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Circuit" table="Circuit">
    <id name="CircuitID">
      <column name="CircuitID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="CJON" column="CJON" type="string" not-null="true"/>
    <bag name="Documents" table="CircuitDocument" cascade="save-update,delete-orphan">
      <key column="CircuitID"/>
      <many-to-many class="Document">
        <column name="DocumentID" not-null="true"/>
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Issue" table="Issue">
    <id name="IssueID">
      <column name="IssueID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="Summary" column="Summary" type="string" not-null="true"/>
    <bag name="Documents" table="IssueDocument" cascade="save-update,delete-orphan">
      <key column="IssueID"/>
      <many-to-many class="Document">
        <column name="DocumentID" not-null="true"/>
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Document" table="Document">
    <id name="DocumentID">
      <column name="DocumentID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="Data" column="Data" type="string" not-null="true"/>
  </class>
</hibernate-mapping>

代码:

using (ISession session = sessionFactory.OpenSession())
{
    var doc = new Model.Document() { Data = "Doc" };

    var circuit = new Model.Circuit() { CJON = "circ" };
    circuit.Documents = new List<Model.Document>(new Model.Document[] { doc });

    var issue = new Model.Issue() { Summary = "iss" };
    issue.Documents = new List<Model.Document>(new Model.Document[] { doc });

    session.Save(circuit);
    session.Save(issue);
    session.Flush();                
}
using (ISession session = sessionFactory.OpenSession())
{
    foreach (var item in session.CreateCriteria<Model.Circuit>().List<Model.Circuit>())
    {
        session.Delete(item);
    }
    //this flush fails, because there is a reference to a child document from issue
    session.Flush();
    foreach (var item in session.CreateCriteria<Model.Issue>().List<Model.Issue>())
    {
        session.Delete(item);
    }
    session.Flush();
}

I have 3 top level entities in my app: Circuit, Issue, Document

Circuits can contain Documents and Issues can contain Documents.

When I delete a Circuit, I want it to delete the documents associated with it, unless it is used by something else. I would like this same behavior with Issues. I have it working when the only association is in the same table in the db, but if it is in another table, then it fails due to foreign key constraints.

ex 1(This will cascade properly, because there is only a foreign constraint from Circuit to Document)
Document1 exists.
Circuit1 exists and contains a reference to Document1.
If I delete Circuit1 then it deletes Document1 with it.

ex 2(This will cascade properly, because there is only a foreign constraint from Circuit to Document.)
Document1 exists.
Circuit1 exists and contains a reference to Document1.
Circuit2 exists and contains a reference to Document1.
If I delete Circuit1 then it is deleted, but Document1 is not deleted because Circuit2 exists.
If I then delete Circuit2, then Document1 is deleted.

ex 3(This will throw an error, because when it deletes the Circuit it sees that there are no other circuits that reference the document so it tries to delete the document. However it should not, because there is an Issue that has a foreign constraint to the document.)
Document 1 exists.
Circuit1 exists and contains a reference to Document1.
Issue1 exists and contains a reference to Document1.
If I delete Circuit1, then it fails, because it tries to delete Document1, but Issues1 still has a reference.

DB:
This think won't let upload an image, so here is the ERD to the DB: http://lh3.ggpht.com/_jZWhe7NXay8/TROJhOd7qlI/AAAAAAAAAGU/rkni3oEANvc/CircuitIssues.gif

Model:

public class Circuit
{
    public virtual int CircuitID { get; set; }
    public virtual string CJON { get; set; }
    public virtual IList<Document> Documents { get; set; }
}
public class Issue
{
    public virtual int IssueID { get; set; }
    public virtual string Summary { get; set; }
    public virtual IList<Model.Document> Documents { get; set; }
}
public class Document
{
    public virtual int DocumentID { get; set; }
    public virtual string Data { get; set; }
}

Mapping Files:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Circuit" table="Circuit">
    <id name="CircuitID">
      <column name="CircuitID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="CJON" column="CJON" type="string" not-null="true"/>
    <bag name="Documents" table="CircuitDocument" cascade="save-update,delete-orphan">
      <key column="CircuitID"/>
      <many-to-many class="Document">
        <column name="DocumentID" not-null="true"/>
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Issue" table="Issue">
    <id name="IssueID">
      <column name="IssueID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="Summary" column="Summary" type="string" not-null="true"/>
    <bag name="Documents" table="IssueDocument" cascade="save-update,delete-orphan">
      <key column="IssueID"/>
      <many-to-many class="Document">
        <column name="DocumentID" not-null="true"/>
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Model" assembly="Model">
  <class name="Document" table="Document">
    <id name="DocumentID">
      <column name="DocumentID" not-null="true"/>
      <generator class="identity" />
    </id>
    <property name="Data" column="Data" type="string" not-null="true"/>
  </class>
</hibernate-mapping>

Code:

using (ISession session = sessionFactory.OpenSession())
{
    var doc = new Model.Document() { Data = "Doc" };

    var circuit = new Model.Circuit() { CJON = "circ" };
    circuit.Documents = new List<Model.Document>(new Model.Document[] { doc });

    var issue = new Model.Issue() { Summary = "iss" };
    issue.Documents = new List<Model.Document>(new Model.Document[] { doc });

    session.Save(circuit);
    session.Save(issue);
    session.Flush();                
}
using (ISession session = sessionFactory.OpenSession())
{
    foreach (var item in session.CreateCriteria<Model.Circuit>().List<Model.Circuit>())
    {
        session.Delete(item);
    }
    //this flush fails, because there is a reference to a child document from issue
    session.Flush();
    foreach (var item in session.CreateCriteria<Model.Issue>().List<Model.Issue>())
    {
        session.Delete(item);
    }
    session.Flush();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

绮烟 2024-10-16 10:49:17

您需要在删除父集合之前清除集合。 delete-orphan 导致删除操作级联到子级,因为子级仍在集合中被引用。该对象在取消引用之前没有可能成为孤儿,因此不会对其进行检查。 本文档中的第 10.11 节解释得很好,但仍然令人困惑。据我了解,删除父级时,delete-orphan 有两种可能的结果:

  1. 如果父级集合中仍引用子级,则将其删除。
  2. 如果子级不再在父级集合中被引用,那么如果它是孤儿,即没有其他引用,则将其删除。

我认为这会按预期工作:

    using (ISession session = sessionFactory.OpenSession())
    {
        foreach (var item in session.CreateCriteria<Model.Circuit>().List<Model.Circuit>())
        {
            item.Documents.Clear();
            session.Delete(item);
        }
        session.Flush();
        foreach (var item in session.CreateCriteria<Model.Issue>().List<Model.Issue>())
        {
            item.Documents.Clear();
            session.Delete(item);
        }
        session.Flush();
    }

You need to clear the collection before deleting the parent. delete-orphan is causing the delete operation to be cascaded to the child because the child is still referenced in the collection. The object does not have the potential to be an orphan until it is derefenced, so that is not checked. Section 10.11 in this documentation explains it well but it's still confusing. As I understand it, delete-orphan has two possible outcomes when the parent is deleted:

  1. If the child is still referenced in the parent's collection then it is deleted.
  2. If the child is no longer referenced in the parent's collection then it is deleted if it is an orphan, i.e. has no other references.

I think this will work as desired:

    using (ISession session = sessionFactory.OpenSession())
    {
        foreach (var item in session.CreateCriteria<Model.Circuit>().List<Model.Circuit>())
        {
            item.Documents.Clear();
            session.Delete(item);
        }
        session.Flush();
        foreach (var item in session.CreateCriteria<Model.Issue>().List<Model.Issue>())
        {
            item.Documents.Clear();
            session.Delete(item);
        }
        session.Flush();
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文