我正在尝试编写正确的地图配置,这样我就可以在多对多关系设置中仅从一侧删除。
下面是我的Map和Entity类的代码结构以及实际程序(伪)代码和SQL模式。相当简单直接。
我们有一个人员表和一个文件表。然后我们有一个personfile表,因为一个人可以有很多文件,同样,一个文件可以分配给很多人。
现在,当我删除个人记录时,将删除personfile中的相关记录和属于该人的文件。到目前为止一切都很好。
如果我删除了一个文件(如下面的Program.cs所示),我希望它从人文件和文件中删除,但不是从人那里删除。
然而,按照我设置的方式,NHibernate只调用文件表上的delete,这会导致错误。例如
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`personfile`, CONSTRAINT `FK_PersonFile2` FOREIGN KEY (`FileID`) REFERENCES `file` (`FileID`))
我确实将Cascade.Delete()添加到了FileMap,但是当我这样做时,从文件表中删除也会从人员表中删除。
重申一下,我最终想要的是调用Delete(文件),而delete(文件)又会删除personfile表和文件表中的记录,但不会删除人员表。
我是否应该继续获取人员记录的路线,然后从person.Files []集合中删除文件记录,然后调用SaveOrUpdate()?
考虑一下情景。
首先确保我们在所有表格中都有良好的数据。
mysql> SELECT f.FileID, p.PersonID, p.Name, f.Filename
-> FROM personfile pf
-> LEFT OUTER JOIN file f on pf.FileID = f.FileID
-> LEFT OUTER JOIN person p on pf.PersonID = p.PersonID
-> ;
+--------+----------+------+-------------+
| FileID | PersonID | Name | Filename |
+--------+----------+------+-------------+
| 1 | 1 | John | Apples.jpg |
| 2 | 1 | John | Oranges.jpg |
| 3 | 2 | Bob | Grapes.jpg |
+--------+----------+------+-------------+
3 rows in set (0.00 sec)
现在处于正常情况下,如果你试图仅删除文件(这是NHibernate根据我的设置尝试做的事情),这就是预期会发生的事情。
mysql> DELETE FROM file WHERE file.FileID = 2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`pe
rsonfile`, CONSTRAINT `FK_PersonFile2` FOREIGN KEY (`FileID`) REFERENCES `file` (`FileID`))
我希望NHibernate做的是这样的事情,首先删除关系表中的记录,然后删除atual文件表中的记录。 只要最终结果相同,查询就不必具体。
mysql> DELETE pf, f
-> FROM personfile pf
-> LEFT OUTER JOIN file f on pf.FileID = f.FileID
-> LEFT OUTER JOIN person p on pf.PersonID = p.PersonID
-> WHERE pf.FileID = 2
-> ;
Query OK, 2 rows affected (0.05 sec)
上述删除的结果有效。
mysql> SELECT f.FileID, p.PersonID, p.Name, f.Filename
-> FROM personfile pf
-> LEFT OUTER JOIN file f on pf.FileID = f.FileID
-> LEFT OUTER JOIN person p on pf.PersonID = p.PersonID
-> ;
+--------+----------+------+------------+
| FileID | PersonID | Name | Filename |
+--------+----------+------+------------+
| 1 | 1 | John | Apples.jpg |
| 3 | 2 | Bob | Grapes.jpg |
+--------+----------+------+------------+
2 rows in set (0.00 sec)
Program.cs的
File file = db.Session.Load<File>(2);
session.Delete(file);
transaction.Commit();
映射
public class FileMap : ClassMap<File>
{
public FileMap()
{
Id(x => x.FileID)
.GeneratedBy.Identity();
Map(x => x.Filename)
HasManyToMany(x => x.Persons)
.Table("PersonFile")
.Inverse()
.ParentKeyColumn("FileID")
.ChildKeyColumn("PersonID");
}
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.PersonID)
.GeneratedBy.Identity();
Map(x => x.Name)
HasManyToMany(x => x.Files)
.Table("PersonFile")
.Cascade.Delete()
.ParentKeyColumn("PersonID")
.ChildKeyColumn("FileID");
}
}
实体
public class File
{
public virtual uint FileID { get; set; }
public virtual string Filename { get; set; }
public virtual IList<Person> Persons { get; set; }
}
public class Person
{
public virtual uint PersonID { get; private set; }
public virtual string Name { get; set; }
public virtual IList<File> Files { get; set; }
}
SQL
CREATE TABLE `file` (
`FileID` int(11) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`FileID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `person` (
`PersonID` int(11) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`PersonID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `personfile` (
`PersonID` int(11) unsigned NOT NULL DEFAULT '0',
`FileID` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`PersonID`,`FileID`),
KEY `FK_PersonFile2` (`FileID`),
CONSTRAINT `FK_PersonFile1` FOREIGN KEY (`PersonID`) REFERENCES `person` (`PersonID`),
CONSTRAINT `FK_PersonFile2` FOREIGN KEY (`FileID`) REFERENCES `file` (`FileID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
答案 0 :(得分:2)
尝试调用File.Persons.Clear()
从文件中删除链接的人员,然后再将其删除。
我对这个要求感到好奇;第二句似乎与它相矛盾,因为您可以删除链接到与您要删除的人不同的人的文件记录。
我们有一个人员表和一个文件表。然后我们有一个personfile表 因为一个人可以有很多文件和 同样,可以分配文件 很多人。
现在,当我删除一个人的记录时, personfile和中的相关记录 属于该人的文件 删除。到目前为止一切都很好。
答案 1 :(得分:0)
File.Persons.Clear();
Session.Flush();
Session.CreateQuery("delete from File").ExecuteUpdate();
适合我!