7
votes

Comment verrouiller une table entière dans Symfony2 avec Doctrine2?

J'ai besoin de verrouiller une table entière (pas une seule rangée) avec la doctrine, je voudrais le faire sans requêtes natales si possible.

La documentation de Le verrouillage pessimiste ne décrit que comment verrouiller des entités spécifiques via ces méthodes:

  • EntityManager # Trouver
  • EntityManager # LOCK
  • Query # SetockMode

    J'ai une transaction qui doit insérer une ligne dont les valeurs dépendent des valeurs du reste des lignes de la table, de sorte que je dois empêcher deux transactions effectuant en même temps sur cette table.

    J'utilise une démarcation de transaction explicite, qui est censée bien fonctionner avec le verrouillage (selon la documentation ci-dessus).

    note : le verrouillage optimiste n'est pas assez bon dans ce cas, je ne peux pas vous permettre de réessayer la transaction. Outre la requête n'est pas censée être lente, la performance n'est pas une question.

    EDIT : Je vais donner un exemple. Imaginez que vous souhaitiez construire un auto_incrènement, et vous devez sélectionner Max () à partir de la table pour obtenir le résultat précédent afin d'insérer le prochain. Vous devez vous assurer que les deux transactions n'essaient pas d'insérer la même valeur au cas où elles sélectionneront max () en même temps.

    Je cherche une solution générale à ce problème lorsque vous n'êtes pas bon, par exemple avec des chaînes, ou plusieurs colonnes, hachages ou quel que soit le calcul que vous devez faire sur le jeu de lignes précédent.

    Le verrouillage est une solution solide et, contrairement au verrouillage optimiste, vous n'avez pas à réessayer sur des erreurs.

    Alors, y a-t-il un moyen d'utiliser le verrouillage de table dans la doctrine?


2 commentaires

IMO, une meilleure option serait d'ouvrir une transaction, de récupérer les données de la table dont vous avez besoin via une requête SELECT, puis effectuez la mise à jour.


Le problème est que si une seconde transaction vient avant la première validation de la deuxième transaction insérerait une valeur non valide. Le résultat de la deuxième transaction doit dépendre de la ligne insérée par le premier. C'est pourquoi j'ai besoin de verrouillage, les transactions ne doivent pas se chevaucher pour cette table.


3 Réponses :


1
votes

En regardant la documentation dans la doctrine 2.x, je ne pense pas qu'il existe une manière prise en charge de verrouiller toute la table. Vous pouvez bien sûr essayer de verrouiller tous les enregistrements via la doctrine individuellement, mais ce serait lourd et n'est pas vraiment une bonne idée.

Au lieu de cela, j'utiliserais la doctrine Entity Manager pour exécuter SQL brut sur la base de données ...

$ em-> getconnection () -> exec ("tables de verrouillage table_name écriture; '); // Verrouiller l'accès en écriture

et ensuite après que vous ayez terminé avec votre mise à jour ...

$ em-> getconnection () -> exec ('déverrouillage des tables;');

EDIT:

de Documentation MySQL sur les serrures de table .. .

  • la session qui contient la serrure peut lire et écrire la table.

  • Seule la session qui détient la serrure peut accéder à la table. Aucune autre session ne peut y accéder jusqu'à ce que la serrure soit libérée.

  • Les requêtes de verrouillage de la table par d'autres sessions Block tandis que le verrouillage de l'écriture est maintenu.

    Je pense que le deuxième point ici est essentiel, seule votre session peut lire / écrire à cette table.


4 commentaires

En outre, le verrouillage individuellement les enregistrements n'empêcherait pas de nouveaux enregistrements beign insérés. OTOH L'instruction de verrouillage n'est pas transactionnelle selon la DOCS, et je ne sais pas comment la doctrine implémente les transactions, mais si elle utilise la transaction de démarrage, cela ne fonctionnera pas.


Tables de verrouillage [Nom_Tbl_Name] verrouille toute la table pour écrire, voir mon édition


Oui, je sais, le seul problème que j'ai avec serrure est qu'il ne joue que lorsque vous effectuez des transactions à l'aide de «Autocommit = 0 ', et je ne sais pas comment la doctrine gère les transactions. Je dois encore le tester. Voir cette dev.mysql.com/doc/ Refman / 5.0 / FR / ...


La doctrine vous oblige à envelopper les appels dans une transaction explicite, et même alors c'est difficile (impossible?) Parce que la doctrine utilise parfois des alias dans ses mises à jour SQL ("Si vos déclarations font référence à une table au moyen d'un alias, vous devez verrouiller la table en utilisant ce même alias "). Dans ma situation, il utilise un alias et le nom de la table dans la seule transaction.



2
votes

éventuellement dupliqué par Doctrine2 Orm Sélectionner pour la mise à jour

Voici quelques indicateurs connexes : xxx

Le paramètre LockMode :: optimiste peut fournir ce dont vous avez besoin.


1 commentaires

En général, la sélection de la ligne de mise à jour n'empêche pas d'inserts simultanés.



4
votes

Suivre les conseils jusqu'à présent, j'ai essayé ceci:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

// calculate $new_number...

// persist $new_number on table_name...
$em->getConnection()->executeUpdate("UPDATE table_name set ...;");    

$em->getConnection()->exec('UNLOCK TABLES;');
$em->refresh($table_name);


0 commentaires