Свободное владение NHibernate: каскадное удаление с одной стороны только в отношениях «многие ко многим» - PullRequest
2 голосов
/ 11 февраля 2010

Я пытаюсь написать правильную конфигурацию карты, которая позволит мне удалять только одну сторону при настройке отношения «многие ко многим».

Ниже приведена структура кода для моего класса 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;

Ответы [ 2 ]

2 голосов
/ 11 февраля 2010

Попробуйте позвонить File.Persons.Clear(), чтобы удалить связанных лиц из файла перед его удалением.

Мне интересно это требование; второе предложение, кажется, противоречит этому, потому что вы можете удалить записи Файла, связанные с другим Лицом, отличным от того, который вы удаляете.

У нас есть таблица персон и таблица файлов. Затем у нас есть личный файл так как человек может иметь много файлов и аналогично, файл может быть назначен много человек.

Теперь, когда я удаляю запись о человеке, соответствующие записи в личном файле и файл, принадлежащий человеку удален. Пока все хорошо.

0 голосов
/ 18 июня 2018
File.Persons.Clear();

Session.Flush();

Session.CreateQuery("delete from File").ExecuteUpdate();

У меня работает!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...