Я пытаюсь написать правильную конфигурацию карты, которая позволит мне удалять только одну сторону при настройке отношения «многие ко многим».
Ниже приведена структура кода для моего класса Map и Entity вместе с реальным кодом программы (psuedo) и схемой SQL. Довольно простой и понятный.
У нас есть таблица персон и таблица файлов. Затем у нас есть таблица личных файлов, так как у человека может быть много файлов, а также файл может быть назначен многим людям.
Теперь, когда я удаляю запись о человеке, соответствующие записи в личном файле и файле, принадлежащем этому человеку, удаляются. Пока все хорошо.
Если я удаляю файл (как показано ниже в Program.cs), я хочу, чтобы он удалялся из персонального файла и файла, но НЕ из персонального.
Однако, как у меня все настроено, NHibernate вызывает только удаление таблицы файлов, что вызывает ошибку. Например.
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, но когда я это сделал, удаление из таблицы файлов также удаляет из таблицы person.
Чтобы повторить, я в конечном итоге хочу вызвать Delete (файл), который, в свою очередь, удалит записи из таблицы персоналий и файлов, но НЕ из таблицы персон.
Должен ли я вместо этого идти по пути получения записи о человеке, а затем удалить запись файла из коллекции 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 делал что-то вроде этого, где сначала удаляются записи в таблице отношений, а затем записи в обычной таблице файлов.
Запрос не должен быть конкретным, если конечный результат такой же.
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();
Mapping
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");
}
}
Entity
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;