返回值,抛出异常并回滚事务

时间:2011-09-19 12:11:19

标签: php mysql transactions try-catch return-value

整个“何时抛出异常或返回值”的问题已被提出很多问题(请参阅以下内容仅查看一个示例):

Should a retrieval method return 'null' or throw an exception when it can't produce the return value?

我完全同意主要答案。

现在我的问题来自于在将其应用于更复杂的系统时为上述内容添加更多上下文。我会尽量保持简洁和简单。

我们有一个示例MVC PHP应用程序:

模型A:有一个函数get_car($ id),它返回一个汽车对象。

控制器A具有简单的功能,例如向用户显示汽车

然而,

控制器B有一个复杂的功能,即获取汽车,修改它(比如通过模型A的设置功能之一),并通过整个系统中的其他模型和库基于这些新值更新其他表 - 非常复杂的ay lol

我们现在谈谈我的问题的主要部分:

对于数据完整性,我想使用MySQL事务。这是我遇到“什么是最好的/什么是最佳实践”的场景...

如果找不到汽车或存在SQL错误,我们编写模型A以返回FALSE。 这对控制器A来说很好,因为它只是想知道是否有错误并且爆炸,所以我们只需检查返回值是否正常。

我们现在转到控制器B.控制器B说在调用模型A函数之前进行了一些数据库更新,我们需要回滚错误,因此我们需要使用事务。现在这是我遇到问题的地方。我是否将模型A作为返回值并将其检查或者我将其更改为抛出异常,然后还要重新编写控制器A,因为我们现在需要捕获异常...然后(不是完成了; o))我回滚模型的捕获(但我们如何知道交易是否已被使用?)或者我们是否抓住并重新抛出或允许冒泡到控制器捕获和回滚那里?

我想说的是,如果我有很多模型和控制器与数据库交互,我应该让它们抛出异常然后包装我的所有其他代码,例如控制器函数在try catch中包装模型或库函数扔,或者,我是否让模型“自包含”以整理和处理自己的问题但是如果(对于这个“调用”)一个是打开的话,我该如何回滚一个事务(根据我上面的例子而不是每个时间是一个交易开放...)?如果是这种情况我将不得不让我的所有函数返回一些东西,然后在控制器中检查,因为这是唯一知道是否有开放交易的地方......

所以为了澄清我可以使用try catch来捕获并在控制器中回滚,这没关系,但是如何从“更进一步”执行此操作,例如在模型或库函数中......可以同时调用在和事务期间或者只是作为自动提交正常的MySQL调用?

一个解释的答案会很棒(因为我喜欢理解我为什么要做某事)但如果不是对以下解决方案的最爱投票(我能看到的解决方案):

1)使所有模型和库函数始终返回一个值,然后控制器可以只是bom或try try catch在必要时回滚 - 但这意味着我必须检查每个模型和库的返回值功能无处不在。

2)使所有模型和库函数抛出异常(例如SQL错误)并将每个控制器(可以调用模型和库函数)包装在try catch中,其中catch将在必要时生成或回滚。 ..

另请注意“bom”是推送用户某处或显示一个漂亮的错误(在有人说“其不良做法只是让你的应用程序死...”大声笑)

我希望你能从这里来到这里,并为长期的loooooong问题感到抱歉。

提前致谢 本

1 个答案:

答案 0 :(得分:0)

[对于数据完整性我想要使用MySQL事务“中隐含一个理论问题......因为MySQL历史上并不是非常ACID - PostgreSQL和Oracle都为ACID提供了更强大的支持。但是,回到真正的问题......]

你的(1)和(2)都关注异常与失败返回值,但我的印象是,这不是纠缠异常,错误返回和打开事务的关键部分(有些数据库支持SQL异常)以及)。相反,我专注于保持事务状态与操作模型的函数的嵌套相关联。以下是这方面的一些想法:

  • 无论如何,你可能总会从某些库函数返回错误,因此让模型A返回FALSE并不是真正打破范式,对于错误返回与异常的混合也没有什么特别麻烦。但是,错误返回必须正确冒泡 - 或者如果超出本地地址范围,则转换为异常。
  • 嵌套事务是让一个控制器启动数据库操作并仍然调用也使用事务的应用程序中的其他内容的最明显方式。这允许失败的子子函数仅中止其自己的部分事务,并采用错误返回或异常方法在非SQL端冒泡错误,而关闭的子事务仍然保持合理的匹配状态。这通常需要在数据库之外的代码中进行模拟(这基本上是Django所做的)。
  • 您的代码可以启动一个新的(可能很大的)事务,并跟踪它已经打开的事实,以防止代码中的子子函数尝试重新打开它。
  • 在某些数据库中,代码可以根据数据库会话状态检测事务是否已打开,允许您检查数据库会话状态,而不是在代码中跟踪它。
  • 上述两种方法都允许使用保存点来模拟真正的嵌套事务。
  • 必须非常小心,以避免使用隐式提交调用SQL调用(例如,CREATE TABLE)。对于这个问题,MySQL可能比PostgreSQL更值得警惕。

实现一个大事务方法的一种方法是使用启动事务的高级函数,然后本身调用控制器B需要做的任何事情的顶层。这使得冒泡错误或具有特殊的中止事务异常非常简单。如果没有子功能失败且没有捕获异常,则只有top函数会调用commit而不是abort。子功能不会调用commit。

相反,您可以让所有函数都注意在非SQL端(您的代码)中实现的事务深度,尽管在某些语言中设置比其他语言更难(在Python中使用装饰器非常容易,例如)。如果它们完成并且事务深度为零,这将允许它们中的任何一个调用commit。

希望这有助于某人: - )

相关问题