2010-11-05 2 views
1

Я делаю систему выставления счетов с поддержкой нескольких дочерних компаний, каждая из которых имеет свой собственный набор номеров счетов, поэтому у меня есть таблица с первичным ключом (Дочерняя компания, InvoiceNo)Альтернатива Max (ID) в сложном первичном ключе

Я не могу использовать MySQL auto increment field, так как тогда он будет постоянно увеличивать один и тот же счет для всех дочерних компаний.

Я не хочу, чтобы сделать раздельные таблицы для каждой дочерней компании будет новые дочерние компании добавили, как это необходимо ...

я в настоящее время с помощью «Select Max (ID) Где Дочернее = X», из мой стол и добавление счета в соответствии с этим.

Я использую nHibernate и вкладку Invoice, перед вставкой InvoiceItem, поэтому, если вставка Invoice не выполняется, InvoiceItem не будет выполнен. Но вместо этого я поймаю исключение, повторно заберу Max (ID) и повторю попытку.

В чем проблема с этим подходом? А если есть, то какая альтернатива?

Резон для спрашивать, потому что я прочитал один из ответов на этот вопрос: Nhibernate Criteria: 'select max(id)'

ответ

1

Как вы сказали, что проблема с этим подходом является несколько сеансов может попытаться вставить один и тот же счет-фактура ID. Вы получаете уникальное нарушение ограничений, должны попробовать еще раз, это может потерпеть неудачу, и так далее.

Я решаю такие проблемы, блокируя субсидию во время создания новых счетов-фактур. Однако не блокируйте таблицу, (a) если вы используете InnoDB, есть проблемы, по умолчанию команда lock table будет commit the transaction. (b) Нет причин, по которым счета-фактуры для двух разных субсидий не должны добавляться одновременно с тем, что у них разные номера счетов-фактур.

Что я буду делать в вашей ситуации:

  • Открыть транзакции и убедитесь, что ваши таблицы InnoDB.
  • Заблокируйте субсидию командой SELECT .. FOR UPDATE. Это можно сделать, используя LockMode.UPGRADE в NHibernate.
  • Найти максимальный идентификатор, используя функцию максимального (..) и сделать вставку
  • Подтверждать транзакцию

Это сериализующий все вставки счета-фактуры для одного subsiduary (т.е. только один сеанс может сделать такую ​​вставку сразу , любая вторая попытка будет ждать, пока первая не будет завершена или не вернется), но это то, что вы хотите. Вам не нужны дыры в ваших счетах (например, если вы вставляете идентификатор счета 3485, а затем он терпит неудачу, тогда есть счета-фактуры 3484 и 3486, но нет 3485).

+0

Это похоже на то, что я ищу. Не могли бы вы еще раз прояснить процесс блокировки. Нужно ли мне выбирать * из счетов-фактур, где вспомогательный = x для обновления, а затем другой запрос на том же сеансе, чтобы получить максимальный id? –

+1

Вам понадобится таблица «субсидия», в которой у вас есть один ряд за субсидию. Это будет тот ряд, который вы бы заблокировали, чтобы показать, что для субсидии ведется исключительная операция. 'START TRANSACTION', затем' SELECT id FROM publuary WHERE financeuary_id =? FOR UPDATE', затем 'SELECT MAX (id) FROM invoice ...', 'INSERT ...', 'COMMIT'. –

+0

Итак, если вы заблокируете дочернюю строку в вспомогательной таблице, она не позволит вставлять какие-либо новые записи, которые имеют эту строку в качестве ограничения внешнего ключа? Это верно? –

3

Это очень плохая идея для использования при создании первичных ключей. Мой совет:

  • Не указывайте первичным ключам деловое значение (синтетические ключи);

  • Использовать вторичный механизм для формирования номеров счетов.

Это сделает вашу жизнь намного проще. Механизм формирования номеров счетов-фактур может затем, например, быть таблицей, которая выглядит примерно так:

  • Дочернее предприятие;
  • NextInvoiceNumber.

Это позволит отделить внутреннюю нумерацию от работы базы данных.

С таким механизмом вы снова сможете использовать поля автоматического увеличения, или даже лучше, GUID.

Некоторые ссылки с чтения материала:

http://fabiomaulo.blogspot.com/2008/12/identity-never-ending-story.html http://nhforge.org/blogs/nhibernate/archive/2009/02/09/nh2-1-0-new-generators.aspx

+0

У меня все еще были проблемы с блокировкой на этом столе. Хотя я мог бы реализовать в будущем, чтобы облегчить выполнение проверки безопасности PreLoadEvent. –

+0

Ваши проблемы с блокировкой будут серьезно смягчены, потому что вы блокируете только одну запись. Чтобы иметь согласованный MAX (ID), теоретически вам необходимо заблокировать всю таблицу, потому что то, что вы хотите заблокировать, состоит в том, что вы хотите, чтобы изменился 'SELECT MAX (ID) WHERE Subsidiary =?'. Существует значительная разница. Кроме того, при блокировке вспомогательного стола вы делаете что-то «странное», потому что вспомогательная таблица не имеет ничего общего с таблицей счетов-фактур. Это просто стратегия блокировки, но вы ничего не делаете с данными. –

+0

+1 Хорошее решение :) –

Смежные вопросы