2010-11-05 5 views
1

Je fais un système de facturation, avec le support de plusieurs filiales qui ont chacune leur propre numéro de facture, donc j'ai un tableau avec une clé primaire de (Filiale, InvoiceNoAlternative à Max (ID) dans la clé primaire complexe

Je ne peux pas utiliser le champ d'incrémentation automatique MySQL, car il incrémentera constamment le même nombre pour toutes les filiales.

Je ne veux pas faire des tables séparées pour chaque filiale car il y aura de nouveaux ajoutés comme besoin filiales ou être ...

J'utilise actuellement « Sélectionner Max (ID) Où Filiale = X », de ma table et en ajoutant la facture en fonction de cela.

J'utilise nHibernate, et l'insertion de facture, précède l'insertion InvoiceItem, donc si l'insertion de facture échoue, InvoiceItem ne sera pas exécuté. Mais à la place, je vais attraper l'exception, re-récupérer le Max (ID) et essayer à nouveau.

Quel est le problème avec cette approche? Et le cas échéant, quelle est une alternative?

Le Reson pour demander est parce que je lis une des réponses sur cette question: Nhibernate Criteria: 'select max(id)'

Répondre

1

Comme vous le dites, le problème avec cette approche est de multiples sessions pourraient essayer d'insérer le même numéro de facture. Vous obtenez une violation de contrainte unique, vous devez réessayer, cela peut également échouer, etc.

Je résous ces problèmes en bloquant la subvention lors de la création de nouvelles factures. Cependant, ne verrouillez pas la table, (a) si vous utilisez InnoDB il y a des problèmes qu'une commande lock table par défaut sera commit the transaction. (b) Il n'y a aucune raison pour laquelle les factures pour deux filiales différentes ne devraient pas être ajoutées en même temps car elles ont des numéros de facture indépendants différents.

Ce que je ferais dans votre situation est la suivante:

  • Ouvrir une transaction et assurez-vous que vos tables sont InnoDB.
  • Verrouillez la sous-unité avec une commande SELECT .. FOR UPDATE. Cela peut être fait en utilisant LockMode.UPGRADE dans NHibernate.
  • Trouver l'ID max en utilisant max (..) la fonction et faire l'insert
  • Valider la transaction

Ce sérialise tous les inserts de facture pour une subsiduary (c.-à-seule session peut faire une telle insertion à la fois , toute seconde tentative attendra que le premier soit terminé ou ait été annulé) mais c'est ce que vous voulez. Vous ne voulez pas de trous dans vos numéros de facture (par exemple, si vous insérez l'identifiant de facture 3485 et que cela échoue, il y a les factures 3484 et 3486 mais pas 3485).

+0

Cela ressemble exactement à quelque chose que je cherche, Pourriez-vous éclaircir le processus de verrouillage un peu plus. Aurais-je besoin de sélectionner * à partir des factures où filiale = x pour la mise à jour, puis une autre requête sur la même session pour obtenir l'identifiant max? –

+1

Vous auriez besoin d'une table «subventionnée» où vous avez une rangée par subvention. Ce serait cette rangée que vous verrouilleriez, pour montrer qu'une opération exclusive était en cours pour la subvention. 'START TRANSACTION', puis' SELECT id FROM SUBWATER SUBWELL_ID =? FOR UPDATE', alors 'SELECT MAX (id) FROM facture ...', 'INSERT ...', 'COMMIT'. –

+0

Ainsi, lorsque vous verrouillez la ligne subsidiaire dans la table subsidiaire, elle ne permet pas d'insérer de nouveaux enregistrements ayant cette ligne comme contrainte de clé étrangère? Est-ce exact? –

3

Ceci est une très mauvaise idée à utiliser lors de la génération des clés primaires. Mon conseil est le suivant:

  • Ne donnez pas une signification commerciale aux clés primaires (clés synthétiques);

  • Utilisez un mécanisme secondaire pour générer les numéros de facture.

Cela vous simplifiera la vie. Le mécanisme de génération de numéros de facture peut alors par ex. être une table qui ressemble à quelque chose comme:

  • Filiale;
  • NextInvoiceNumber.

Ceci séparera la numérotation interne du fonctionnement de la base de données. Avec un tel mécanisme, vous pourrez à nouveau utiliser des champs d'incrémentation automatique, ou mieux, des GUID.

Quelques liens avec du matériel de lecture:

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

J'aurais encore des problèmes de verrouillage, sur cette table. Bien que je pourrais mettre en œuvre à l'avenir pour faciliter l'implémentation de la vérification de sécurité PreLoadEvent. –

+0

Vos problèmes de verrouillage seraient sérieusement atténués, car vous ne verrouillez qu'un seul enregistrement. Pour avoir un MAX (ID) cohérent, vous devez théoriquement verrouiller la table entière car ce que vous voulez verrouiller, c'est que vous voulez que la sélection 'SELECT MAX (ID) WHERE Subsidiary =?' Change. Il y a une différence significative. De plus, en verrouillant la table subsidiaire, vous faites quelque chose d'étrange parce que la table subsidiaire n'a rien à voir avec la table des factures. C'est juste une stratégie de verrouillage, mais vous ne faites rien avec les données. –

+0

+1 Bonne solution :) –

Questions connexes