runDb 中的 MaybeT 和事务

Posted

技术标签:

【中文标题】runDb 中的 MaybeT 和事务【英文标题】:MaybeT and Transactions in runDb 【发布时间】:2015-07-09 07:24:41 【问题描述】:

对于我之前关于链接失败的问题,Michael Snoyman 建议我使用MaybeT 来运行它们,因此如果其中任何一个失败,它将与Nothing 短路。

我的印象是runDb 在事务中运行所有内容。那么代码中任何一点的故障不应该自动回滚事务吗?

mauth <- runDb $ runMaybeT $ do
            valid    <- MaybeT $ return $ listToMaybe errs 
            uid      <- MaybeT $ insertUnique u 
            vid     <- MaybeT $ getBy $ UniqueField v -- this step fails but previous insert does not roll back
            auth     <- liftIO $ createAuthToken uid
            return auth

当我运行上面的代码时,getBy 失败但用户仍然被插入。我是否误解了runDb 会在MaybeT 内部的Nothing 上回滚?我需要使用其他一些 Monad 才能工作吗?

感谢您对如何最好地回滚失败的想法。

更新: 这就是我最终按照迈克尔的建议所做的。

mauth <- runDb $ do
          ma <- runMaybeT $ do
                   valid <- ... 
          case ma of
            Just _ -> return ma
            Nothing -> liftIO $ throwIO MyException

现在我需要弄清楚如何在外面很好地捕获这个异常并返回正确的错误消息。

谢谢!

【问题讨论】:

【参考方案1】:

返回Nothing 与失败不同。您需要抛出运行时异常(通过 throwIO 之类的东西),以便 Persistent 将其视为回滚情况。

【讨论】:

谢谢,迈克尔。我已经用似乎有效的 sn-p 更新了我的问题。 酷。查看lifted-base 中的Control.Exception.Lifted 以了解捕获该异常的方法。 谢谢。会做。我还试图弄清楚如何使用EitherT,这样我实际上可以为每个失败返回错误消息,而不仅仅是我的验证错误。这样我可以保持我的验证代码纯净,避免检查其中的唯一性。如果我不能让它工作,我可能会有一个问题:) 万一有人来这里寻找这样的东西。 @AndrewThaddeusMartin 建议我在 Database.Persist.Sql 中使用 transactionUndo,这使我的代码更好,并且不必处理异常。

以上是关于runDb 中的 MaybeT 和事务的主要内容,如果未能解决你的问题,请参考以下文章

从 runDb 捕获异常

Yesod:在 ghci 中运行“runDB”函数时键入实例错误

如何在 Yesod / Persistent 中正确使用 runDB

MySQL/MariaDB中的事务和事务隔离级别

Java中的事务——全局事务与本地事务

如何处理SQL Server事务复制中的大事务操作