2014-06-07 2 views
4

La gestion des transactions la plus pratique pour moi consiste à utiliser une transaction optionnelle pour toute la requête http. Cela signifie que la première instruction SQL doit récupérer la connexion du pool, démarrer la transaction et, une fois le traitement de la requête terminé, la transaction doit être validée (ou annulée si l'exception a été levée) et la connexion doit être fermée. Bien sûr, une gestion plus fine des transactions doit être possible si nécessaire.Lecture 2 transaction par demande

Est-ce que Play 2 prend en charge ce hors-la-boîte? Je peux probablement l'implémenter moi-même, mais je cherche une solution prête.

J'ai regardé l'objet DB, mais il semble que DB.withConnection utilise une nouvelle connexion (et une transaction) à chaque fois. J'utilise Scala et Anorm db library, si c'est important.

+0

Vous devez spécifier au moins quelle approche DB utiliser et dans quelle langue (Java/Scala) – biesior

+1

J'utilise Scala avec Anorm db library. – vbezhenar

+0

Je m'attendais à une solution plus générale sans avoir besoin de modifier les contrôleurs, mais probablement il n'existe pas. Je vais vérifier votre réponse. – vbezhenar

Répondre

0

DB.withTransaction est ce que vous voulez. Tout comme DB.withConnection, il provisionnera une seule connexion à partir du pool de connexions pour toutes les fonctions SQL contenues. Puisque vous voulez utiliser une transaction par requête, il semble que cela aurait plus de sens de l'appeler dans la fonction de contrôleur, et d'exiger que toutes vos fonctions de modèle aient un paramètre de connexion implicite.

Modèle:

object Product { 

    def checkInventory(id: Long)(implicit c: Connection): Int = SQL(...) 

    def decrementInventory(id: Long, quantity: Int)(implicit c: Connection): Boolean = SQL(...) 

} 

object Cart { 

    def addItem(id: Long, quantity: Int)(implicit c: Connection): Boolean = SQL(...) 

} 

fonction contrôleur:

def addToCart(id: Long, quantity: Int) = Action { 

    DB.withTransaction{ implicit connection => 
     if(Product.checkInventory(id) >= quantity && Product.decrementInventory(id, quantity)) { 

     Cart.addItem(id, quantity) 
     .... 
     } else { 
      BadRequest 
     } 
    } 
} 

Avertissement: Ceci est clairement pas une transaction de panier logiquement son, et une simple illustration de l'utilisation des transactions de base de données.

Après Action exemples de composition dans le documentation, vous pouvez faire une action Transaction spéciale que les dispositions de la transaction pour chaque demande automatiquement:

object Transaction extends ActionBuilder[Request] { 
    def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = { 
     DB.withTransaction{implicit connection => block(request)} 
    } 
} 

et de l'utiliser comme Action:

def addToCart(id: Long, quantity: Int) = Transaction { 
    Product.checkInventory(id) 

    ... 
} 

Pièges: Fournir une transaction par requête de cette manière est pratique, en particulier lorsque la plupart de vos contro Les fonctions ller sont censées représenter les actions atomiques. Toutefois, cette méthode ne libère pas le Connection de la transaction tant que le Result n'a pas été renvoyé à l'utilisateur. Cela signifie que si vous renvoyez un grand ensemble de données qui prend beaucoup de temps à servir/restituer pour le client, vous conserverez la connexion pendant plus longtemps que vous n'en avez réellement besoin.

+0

Est-il bon d'avoir tous DB.withConnection en action? Pouvez-vous accomplir une meilleure conception en couches avec moins verbeux avec quelque chose comme un modèle de gâteau ou DaoManager? –

Questions connexes