当 NHibernate 中的引用发生变化时,如何删除引用的对象?
我有两个像这样的实体(仅提取相关内容):
public class Person {
public virtual Address Address
}
public class Address {
}
Person
与 Address
具有多对一关系,或者用对象术语来说,是一个 Person
引用Address
,并且此链接是单向的(Address
没有返回到Person
的引用)。
现在,如果我使用这段代码:
// save a new person with a new address
var person = new Person();
person.Address = new Address();
person.Save();
// change the person's address and save it again
person.Address = new Address();
person.Save();
第一部分执行预期的操作,它会在数据库中创建一个新的 Person
和一个新的 Address
。
但是,第二部分将更新现有的 Person
并创建新的 Address
,但不会删除现在孤立的第一个地址。为了让 NHibernate 认识到我正在更改对新实体的引用并自动“垃圾收集”孤立实体,我需要做什么?或者我必须手动执行此操作?
可能的解决方案
虽然我没有确凿的证据表明这确实发生了,但我认为这个问题的原因是因为对象关系阻抗不匹配。在编程中,我们知道对象保存对其他对象的引用,一旦这些引用丢失,垃圾收集就会到来并删除那些不再有任何引用指向它们的“孤立”对象。在我提供的示例中,Person
具有对 Address
的引用,一旦对 Address
的引用丢失,它将被垃圾收集,假设没有其他 Person
也可以引用它。
在数据库方面,没有垃圾收集的概念。对于数据库来说,仅存在一对多或多对多的关系,或者换句话说,父级与子级的集合。回到 Person
和 Address
,我们看到这是从 Address
到 Person
的一对多关系>(每个人只能有一个地址,但一个地址可以属于很多人,例如一户人家共用一个地址)。数据库将 Address
视为父级,将 Person
视为子级集合。因此,删除 Person
时,Address
不应被删除(换句话说,如果您有一个装有书籍的书架,则删除一本书不应该删除书架)。
那么我们该如何解决这个问题呢?我们必须删除子集合为空的所有父集合。在这种情况下,应删除具有空集合 Persons
的 Address
。这是一条业务规则,因为没有任何内容表明 Address
不能单独存在,但在我正在开发的应用程序中,如果不附加 Address
本身,则没有任何意义对一个人来说,因此应该被消除。但是,该地址完全有可能保持孤立状态,直到其他 Person
出现并引用它。
I have two entities like so (distilled to only what's relevant):
public class Person {
public virtual Address Address
}
public class Address {
}
Person
has a many-to-one relationship with Address
, or in object terms, a Person
references an Address
, and this link is uni-directional (Address
does not have a reference back to Person
).
Now, if I use this code:
// save a new person with a new address
var person = new Person();
person.Address = new Address();
person.Save();
// change the person's address and save it again
person.Address = new Address();
person.Save();
The first part does what it's supposed to, it creates a new Person
and a new Address
in the database.
However, the second part will update the existing Person
and create a new Address
, but it doesn't delete the now-orphaned first address. What do I have to do in order to get NHibernate to recognize that I'm changing the reference to a new entity and automatically 'garbage collect' the orphaned entity? Or do I have to do this manually?
Possible Solution
Although I don't have conclusive evidence that this indeed what's going on, I think the reason for this problem is because of the object-relational impedance mismatch. In programming, we know that objects hold references to other objects, and once those references are lost, garbage collection will come and delete those 'orphaned' objects that no longer have any references pointing to them. In the example I provided, Person
has a reference to Address
, and once the reference to Address
is lost, it will get garbage collected, assuming no other Persons
have a reference to it as well.
On the database side, there is no concept of garbage collection. To the database, there is only one-to-many or many-to-many relationships, or in other words, a parent with a collection of children. Going back to Person
and Address
, we see that this is a one-to-many relationship from Address
to Person
(each person can only have one address, but an address can belong to many people, such as a household of people that share a common address). The database considers the Address
the parent and the Person
as a collection of children. Thus, it makes sense that by deleting a Person
, the Address
should not be deleted as well (put another way, if you have a bookshelf with a collection of books, deleting a book should not delete the bookshelf).
So how do we fix this? We'll have to delete any parents where their child collections are empty. In this case, an Address
with an empty collection of Persons
should be deleted. This is a business rule, as nothing states that an Address
cannot exist by itself, but in the application I'm developing, an Address
by itself means nothing if it's not attached to a person, and thus should be eliminated. However, it's entirely possible for the address to remain in an orphaned state until some other Person
comes along and references it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在关系上设置cascade =“all-delete-orphan”
set cascade="all-delete-orphan" on the relationship
在 NHibernate 中没有合理的方法可以做到这一点,即使您从一侧(地址)映射关系也是如此。这相当于要求当一个对象的子集合之一为空时自动删除该对象。我不遵循您在可能的解决方案中的想法,但您认为这是业务规则是正确的。
当然,有一些“坏”方法可以实现这一目标。如果数据库中有外键约束,则可以在更改引用时删除地址并吞掉异常。如果删除成功,那么它就是一个孤儿,如果没有,那么仍然有 Person 引用它。
地址通常位于关系的多方,即一个人可以有多个地址。在我看来,这是一个更好的方法。您最终会在地址表中得到重复的地址,但这只是重复数据而不是重复数据。
There's no reasonable way to do this in NHibernate, even if you map the relationship from the one (Address) side. It's equivalent to asking that an object be automatically deleted when one of its child collections is empty. I don't follow your thinking in your possible solution, but you're correct that this is a business rule.
There are, of course, "bad" ways to accomplish this. If there were a foreign key constraint in the database, you could delete an Address when you change a reference and swallow the exception. If the delete succeeds then it was an orphan, if not then there are still Persons referencing it.
Address is usually on the many side of a relationship, i.e. Person can have multiple addresses. This is, imo, a much better approach. You do end up with duplicate addresses in the Address table but this as repeating data not duplicate data.