设计与数据库相关的方法,最好返回:true / false或受影响的行?


10

我有一些方法可以在数据库中执行某些数据更改(插入,更新和删除)。在ORM我使用这些类型的方法返回行INT影响值。我应该为“我的方法”返回什么,以指示操作的成功/失败状态?

考虑返回一个代码int

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

然后用法:

A2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

与使用boolean相比:

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

然后用法:

B.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

哪个(A或B)更好?还是各有利弊?


在您的“ A.2”中。如果零行受到影响,那么如果需要影响零行又为什么会失败呢?换句话说,如果没有数据库错误,为什么会失败?
杰德2014年

5
“不成功”和“受影响的零行”之间在语义上有区别吗?例如,在删除客户的所有订单时,“客户不存在”和“客户没有订单”之间是有区别的。
Cephalopod 2014年

您是否认为意外删除零行?在那种情况下扔。
usr 2014年

@Arian我认为这是我真正的问题。我想我选择了B,因为有了A,我的代码现在包含检查某些地方的0和其他地方的-1的效果
Hoang Tran 2014年

Answers:


18

另一种选择是返回结果对象而不是基本类型。例如:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

这样,如果由于某种原因需要返回受影响的行数,只需在OperationResult中添加一个方法即可:

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

这种设计使您的系统得以扩展并包括新功能(了解受影响的行),而无需修改现有代码。


2
+1“这种设计使您的系统可以扩展并包含新功能(了解受影响的行),而无需修改现有代码”,这是思考此类问题的正确方法。
通知2014年

我在旧项目中做过类似的事情。我有包装请求和结果对象。两者都使用“合成”来包含更多细节。两者都有基本数据。在这种情况下,Result对象具有状态码和msg字段。
告知2014年

特别是对于使用ORM的系统,我将其称为最佳实践。有问题的ORM可能已经包含此类结果对象类型!
Brian S

只要看一下OP的示例,我能想到的就是deleteById会在某个时候返回2 :)所以绝对是自定义类型,是的。
Deadsven 2014年

我喜欢这种方法。我认为这是无障碍的,涵盖了我的两种方法,并且可以修改/扩展(将来,如有需要)。我唯一能想到的缺点是它需要更多代码,尤其是当它出现在我的项目中间时。我将其标记为答案。谢谢。
Hoang Tran 2014年

12

返回受影响的行数更好,因为它提供了有关操作如何进行的其他信息。

没有程序员会责怪您,因为他/她必须编写以下代码来检查他们在操作过程中是否有所更改:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

但是当他们不得不知道受影响的行数时,他们会怪你,他们意识到没有办法获得该行数。

顺便说一句:如果“失败”是指语法查询错误(在这种情况下,受影响的行数显然为0),则抛出异常会更合适。


4
-1是因为您给的原因。提供其他信息并不总是一个好主意。通常,更好的设计可以使呼叫者只需要它,而仅此而已。
2014年

1
@MainMa不能阻止创建重载方法。另外,在本机语言(例如C系列)中,行数可以直接在逻辑中使用(0为false,其他为true)。
PTwr

@PTwr:因此,为应对该方法返回太多信息这一事实,您建议创建一个重载?这似乎不正确。至于“零为假,非零为真”,这不是我的评论重点,在某些语言中是一种不良做法。
阿森尼·穆尔琴科(Arseni Mourzenko)2014年

1
可能我是被宠坏的程序员之一,因为我不认为使用任何技巧都可以视为一种好习惯。事实上,每当我不得不面对别人的代码,我很感谢谁写的,也没有使用任何技巧。
proskor 2014年

4
您应该始终在此处返回受影响的行数!!!!由于您可能无法知道行数= 0是否失败,或者行数= 3是否成功?如果有人要插入3行而仅插入2行,则返回true,但这是不对的!如果有人希望update t set category = 'default' where category IS NULL受影响的行数甚至为0,也将是成功的,因为即使没有行受到影响,现在也没有类别的项目!
Falco 2014年

10

我不推荐任何一个。相反,成功时不返回任何值(无效),失败时抛出异常。

这与我选择声明某些私有类成员的原因完全相同。它还使功能更易于使用。更多的运营环境并不总是意味着更好,但它的确意味着更加复杂。您承诺的越少,您就越能抽象化,客户就越容易理解,并且在选择实现方式时拥有更多的自由。

问题是如何指示成功/错误。在这种情况下,通过抛出异常来表示失败并在成功时不返回任何消息就足够了。为什么我必须提供超出用户需求的内容?

失败/例外情况可能发生,然后您必须对其进行处理。是否使用try / catch进行操作或检查返回码,都取决于样式/个人喜好。try / catch背后的思想是:将正常流与异常流分开,并让异常冒泡到可以最适当地处理它们的层。因此,正如许多人已经指出的那样,这取决于失败是否真的很特殊。


4
-1为什么不返回任何可以带来无额外副作用的,无负面影响的东西?
FreeAsInBeer 2014年

7
出于完全相同的原因,我选择声明某些私有类成员。它还使功能更易于使用。更多的运营环境并不总是意味着更好,但它的确意味着更加复杂。您承诺的越少,您就越能抽象化,客户就越容易理解,并且在选择实现方式时拥有更多的自由。
proskor 2014年

1
那么,用户实际上需要获取哪些数据?问题是如何指示成功/错误。在这种情况下,通过抛出异常来表示失败并在成功时不返回任何消息就足够了。为什么我必须提供超出用户需求的内容?
proskor 2014年

4
@proskor:例外是例外情况。在这种情况下,“失败”可能是预期的结果。无论如何,将其作为一种潜在的替代方法提出,但是这里没有足够的信息可以提出建议。
尼克·巴恩斯

1
-1异常不应作为正常程序流程的一部分。目前尚不清楚在错误的上下文中“失败”是什么意思,但是数据库调用上下文中的异常应归因于数据库中发生的异常。影响零行也不例外。不能解析,查询不存在的表等错误的查询将是一个例外,因为数据库引擎会阻塞它并抛出。

2

“比这更好吗?” 当两个替代方案做的不同时,这不是一个有用的问题。

如果您需要了解受影响的行数,则必须使用版本A。如果不必,则可以使用版本B-但是自从减少代码编写工作后可能获得的任何优势都已经消失了,因为您不方便将两个版本发布到在线论坛!

我的观点是:哪种解决方案更好,完全取决于您对此应用程序的具体要求,并且您知道这些情况比我们要好得多。没有全行业的,安全易用的,最佳实践,不需额外-GET-烧制过它的选择是更好一般 ; 您必须自己考虑。对于像这样容易修改的决定,您也不需要花费太多时间思考。


我同意这在很大程度上取决于应用程序要求。但是,这种情况似乎不是很独特,我不是在寻找万能的药,而是别人在处理相同/相似的事物上的经验(也许这个问题有点误导,我喜欢A以外的建议) / B)
Hoang Tran 2014年

1

可维护软件设计中的两个最重要的原则是KISSYAGNI

  • :保持简单,愚蠢
  • YAGNI:您不需要它

引入您现在不需要的逻辑几乎从来不是一个好主意。在其他许多人中,StackExchange的共同创始人Jeff Atwood 对此发表了看法,以我的经验,他和这些概念的其他支持者是完全正确的。

您添加到程序中的任何复杂性都需要付出一定的成本,并且需要长期支付。该程序变得更难阅读,更复杂,更容易出现漏洞。不要为“以防万一”而添加东西。这是一种错误的安全感。

您很少会在第一次就获得正确的代码。变化是不可避免的;当未来事实与预期不同时,添加推测性逻辑为未来的未知意外事件进行防御性准备实际上并不能保护您不必重构代码。维护不必要/偶然的逻辑比起以后进行重构以增加缺少的功能,更多的是可维护性问题。

因此,由于您的程序现在似乎需要知道该操作是成功还是失败,因此建议的解决方案B(返回一个布尔值)是正确的方法。如果需求发生变化,您以后可以随时对其进行重构。该解决方案最简单,复杂度最低(KISS),可满足您的需要,仅此而已(YAGNI)。


-1

整行或错误状态

考虑至少返回返回的全部行,作为运行时选项。在数据库插入中,您可能需要检查插入的数据-因为它通常与您发送给数据库的数据有所不同;常见的示例包括自动生成的行ID(应用可能会立即需要该行ID),DB确定的默认值以及使用触发器时的触发器结果。

在另一方面,如果你并不需要返回的数据,那么你也不需要受影响的行数,因为如果有错误,不是为0的结果有帮助的,那么你就需要返回一种什么样的错误发生的方式与您的项目错误处理原则(异常,数字错误代码等)一致;但是有一些有效的查询会正确影响0行(即,如果实际上没有任何行,则“删除所有过期的订单”)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.