支持和反对存储过程中的业务逻辑的论据是什么?
17 回答
反对存储过程:编程空间中的业务逻辑
我非常看重表达的力量,我发现 SQL 空间并不是那么富有表现力。使用手头上最好的工具来完成最合适的任务。摆弄逻辑和高阶概念最好在最高级别完成。因此,存储和海量数据操作最好在服务器级别完成,可能在存储过程中。
但这取决于。如果您有多个应用程序与一种存储机制进行交互,并且您希望确保它保持其完整性和工作流程,那么您应该将所有逻辑卸载到数据库服务器中。或者,准备好管理多个应用程序中的并发开发。
我坚决反对。最大的原因之一是earino 所说的第一个原因——它住在一个地方。你不能很容易地将它集成到源代码管理中。让两个开发人员同时处理存储过程几乎是不可能的。
我的另一个主要抱怨是 SQL 不太擅长表示复杂的逻辑。您没有范围的概念,代码往往被复制粘贴,因为重用代码的能力较低(与 OO 语言相反)。
您必须让开发人员访问数据库才能在那里进行开发。在我从事过数据工作的许多组织中,人们与开发人员处于不同的世界,拥有不同的权限等。在这些情况下,将开发人员排除在数据库之外会更加困难。
我的学派认为,只要业务逻辑:
- 住在一个地方
- 正确记录的地方
- 通过松散耦合的服务提供适当的访问
- 通过已发布的抽象接口
我不在乎逻辑是否存在于存储过程、J2EE 中间层、剪辑专家系统或其他任何地方。无论您将我们的业务逻辑存储在哪里,“痛苦守恒定律”都将保证有人会说这是错误的想法,因为组件/存储库 X 需要替换为技术/方法 Y。
一些想法:请注意这是一个以 Java 为中心的回应,但这是我最近(过去 10 年)经验的大部分内容
(1) 由一个(大)开发人员团队进行并发开发。如果您的应用程序足够复杂,以至于每个开发人员都无法设置自己的私有数据库版本(带有参与的链接/参考数据/等...),那么很难让整个开发团队都在工作同一组 PL-SQL(例如)包同时存储在共享的 DEVL DB 中?然后,当人们进行更改时,您在使用无效程序/代码与表不匹配的数据库中工作时遇到了困难(我的经验)......
作为一名 Java 架构师,我认为让每个开发人员在他们的桌面上拥有一个私有 JBoss 实例并轻松地使用他们自己的一组功能,并以他们自己的节奏进行集成而不影响其他人……这让我更容易使用 . ..
(2) 持续集成工具集虽然在 DB 世界中存在一些类似的“概念”,但我的经验告诉我(我在这里选择了我目前最喜欢的同类产品)的组合:
- mvn - 构建系统
- junit - 自动化单元测试
- nexus - repo manager(管理工件的生命周期版本、快照和发布)
- 哈德森 - ci 构建服务器
- 声纳 - 静态分析工具 / 代码覆盖率报告 / 更多
使用上述所有方法(免费工具)运行大型项目可以以一致/简单的方式将 XP 交付给大众,并对整个 IT 员工实施质量控制。Oracle / PL-SQL 没有可匹配的工具集
(3) 工具/库/等等... Java 可以访问其他平台无法访问的一组惊人的服务——有些是免费的,有些不是。即使是基本的,比如 log4j(是的,他们有它用于 PL/SQL,但是 pulease...它几乎不一样)允许开发人员创建可以动态更改的灵活可调的日志记录(非常适合调试) . 自动化 API 文档(通过 javadoc)。自动化的单元测试覆盖率报告。具有集成调试器/自动部署到应用服务器的令人难以置信的 IDE (Eclipse)。一个与阳光下各种类型的服务交互的 API,做任何事情的开源库,以及每个供应商 100% 的支持
(4) 服务的重用。有人评论的是真的。如果您有重型数据驱动的业务规则 ,那么您可以争辩说这些规则应该存在于 DB 层中。为什么?以防止中间层都必须复制该逻辑。
但是对于不是数据驱动或足够复杂的业务规则也可以这样说,OO 是更自然的选择。如果您将所有业务逻辑都粘贴在数据库中,那么它们只能通过数据库获得。
- 如果您想在客户端或中间应用程序层完成验证并保存到数据库的往返行程怎么办?
- 如果您想在中间层缓存只读数据(为了性能)并让业务规则针对缓存的数据执行怎么办?
- 如果你有一个不需要数据库访问的中间层服务,或者你有一个可以提供自己数据的客户端怎么办?
- 如果业务规则的数据依赖部分需要访问外部服务怎么办?然后以如下所示的碎片化业务逻辑结束:
一世
retCode = validateSomeDate(date);
if (retCode == 1) then
evaluateIfCustomerGetsEmail(...)//probably more stored proc invocations here...
sendEmailMsg(....)
else if (retCode == 2) then
performOtherBizLogicStuf(...) //again, may need data, may not need data
triggerExternalsystemToDoSomething(...) //may not be accessible via PL/SQL
fi
我敢肯定,我们都见过像上面那样编写的系统,并且不得不在凌晨 2 点调试它们。当业务逻辑在各层之间分散时,很难对复杂流程有一个连贯的认识,并且在某些情况下它变得无法维护。
“你不能很容易地将它集成到源代码管理中。” - 如果您将创建存储过程的代码放入受版本控制的脚本中,那么反对意见就会消失。如果您遵循 Scott Ambler 的敏捷数据库理念,那正是您应该做的。
并非所有开发人员都是优秀的数据建模者。我可以想到由那些认为涉足 SQL 知识使他们成为数据库专家的开发人员创建的可怕模式。我认为让开发人员与 DBA 和数据建模师一起工作很有价值。
如果只有一个应用程序使用数据库,我会说业务逻辑可以出现在中间层。如果很多应用程序共享数据库,也许最好把它放在数据库中。
SOA 提供了一种中间方式:服务拥有自己的数据。只有服务可以访问数据;获取数据意味着通过服务。在这种情况下,可以将规则放在任何一个地方。
应用程序来来去去,但数据仍然存在。
不将业务逻辑存储在存储过程中的另一个原因 - 数据库的扩展能力有限。数据库成为瓶颈是很常见的情况,这就是为什么尽可能多地承担数据库负载是个好主意。
业务逻辑应该封装在一个地方。我们可以保证逻辑始终运行并始终如一地运行。使用涉及数据库实体的所有活动都必须运行的类,我们可以保证所有验证都正确运行。此代码有一个地方,项目中的任何开发人员都可以轻松打开此类并查看逻辑(因为文档可能并且确实会过时,因此代码是唯一可靠的文档形式)。
这对于存储过程来说很难做到。您可能有多个存储过程处理同一个表。将多个存储过程链接在一起以使逻辑仅驻留在一个存储过程中变得笨拙。那就是一击。您如何确定数据库中“围绕实体 X 的所有业务规则是什么”?寻找数千个试图追踪它的存储过程,玩得开心。
第二是您将业务逻辑与持久性机制联系起来。您可能不会将所有数据存储在同一个数据库中,或者一些数据可能驻留在 XML 等中。这种类型的不一致对开发人员来说是困难的。
如果逻辑仅驻留在数据库中,则很难执行验证。您真的调用 sproc 来验证数据输入表单上的每个字段吗?验证规则和业务逻辑是近亲。这个逻辑都应该在同一个地方执行!
我的几点观察:
支持存储过程:
经过一段时间的项目生命,在大多数情况下,瓶颈是数据库而不是 Web 服务器 - 存储过程要快得多
使用带有 ORM 生成的 sql 查询的 sql profiler 非常困难;使用存储过程很容易
您可以立即为存储过程部署修复程序,而无需服务窗口
就性能而言,优化存储过程比优化 ORM 代码更容易
您可能有许多应用程序使用相同的数据库/存储过程
任何复杂的数据场景都是对存储过程的投票
支持应用程序/ORM:
您可以使用代码存储库(使用存储过程仍然可行,但价格昂贵)
java/c#语言是更好的表达业务逻辑的工具
java / c#更容易调试(动态生成的ORM sql除外)
数据库引擎的独立性(但是,一个项目不太可能将数据库更改为另一个)
ORM 提供易于使用的数据模型
在我看来:对于大型项目 - 使用存储过程,对于其他项目 - 应用程序/ORM 可以正常工作。
归根结底,唯一重要的是数据库。
+:SQL server 有时会优化代码
+:你被强制传递参数,这限制了 SQL 注入问题
-:您的代码依赖于单个数据库(有些数据库甚至没有 SP)
-:要更改代码,您需要连接到数据库
-:逻辑没有组织好
我个人反对它,但我不得不在一个非常繁忙的网站上使用它一次。在 MS SQL 中使用 SP 带来了巨大的好处,但是一旦我实现了缓存,这些好处就不再那么大了。
有句话...
当你只有一把锤子时,一切看起来都像钉子。
在我看来,没有一个答案可以适合所有情况。在我看来,很多人只是假设将业务逻辑放入数据库中总是错误的。
已经做了很多工作来使事务处理,尤其是批量操作,在数据库端完成时非常高效。自从大多数反对数据库的意见形成以来,数据库中的代码管理也得到了极大的改善。
我认为将数据库服务器视为一个持久层是错误的。如果您的处理活动在数据库服务器上完成时效率最高,那么就在那里进行。
如果没有,那就在别处做。
这一切都归结为最适合您目前正在开发的应用程序、与您合作的团队以及雇用您的客户。
那只是我的2美分。
您可以在业务逻辑层中对业务逻辑进行单元测试。如果它完全独立,则可以模拟持久性操作,因此您只测试 BL。存储过程比 linq 和 c# 更难维护/调试/单元测试。
DBMS != 应用服务器
- 函数式编程(数据库存储过程)与 OOP。对于大型程序,OOP 只是标准。
- IDE - eclipse、intellij、netbeans 以及所有用于调试、测试和分析的插件仅适用于真正的编程语言。静态代码工具。
- 如果您为 PLSQL & co 获得版本控制。是很棒的。如果您直接从您的 IDE 获得“同步视图” - 您是真正的幸运者。
- 扩展您的系统。因为数据库系统是地狱。其他“节点”需要昂贵的硬件。复制。并且可能为每个节点提供许可证。不要忘记您仍处于“函数式编程”中,理解和维护此类系统的努力要大得多。
- 你被你的数据库卡住了,尝试改变或添加来自另一家公司的新数据库
- 等等...
业务逻辑的存储过程在今天是不好的做法。改为使用 3 层架构。
有不同种类的“业务逻辑”。考虑根据它与其他层或服务的关系对其进行分区。以下是从 MVC 角度来看的一些经验法则:
a)在数据库(存储过程)中,如果它主要与数据相关,并且可以通过连接和相对简单的 WHERE 和 SELECT 子句来完成。
b)在控制器中,如果它主要与路由或调度相关;即在屏幕或资源选择方面进行更大规模的 UI 流控。
c)在模型或视图模型中,如果它涉及复杂或复杂的计算和/或条件。
d)在视图(例如 Razor)中,如果它主要是显示问题,例如“友好”的重新格式化和相对简单的实现。(如果它很复杂,请考虑将其放入视图模型中。)
我的经验法则(一旦我通过思考这个问题认识到它是什么)是存储过程应该包含确保数据库内数据完整性的代码,无论存储过程是否禁止某些数据插入、删除或修改,或者是否需要进行其他更改为了数据的一致性。业务逻辑,尤其是无法在少数基于集合的操作中实现的逻辑,应该在其他地方实现。数据库不是应用程序。数据库应该是关系和约束的最终权威,即使业务规则也在其他地方实现,例如,在用户界面代码中提供反馈,以减少来自 Web 服务器的回发或消除对繁忙服务器的其他不必要的访问。可以争论是否“数据一致性” 包括复杂业务规则复杂处理的结果,但我认为通常理解上下文后就很清楚了。并非所有业务规则都作为数据关系或约束来实现。并非存储过程中的所有操作都比在单独进程中运行的代码更快,即使在跨网络的单独机器上运行的进程上也是如此。我最近做了一个演示,展示了 SSIS 中的许多操作,例如 (INSERT INTO () SELECT FROM) 在 SSIS 中通过网络在单独的机器上运行比在存储过程中运行更快(这也将结果插入数据库)通过网络)。这是一个几乎令人难以置信的结果(其中 SSIS 比原始 SQL 语句更快),并证明任何性能问题的最佳优化的发现来自现实(测试),而不是来自仅基于少数概念的逻辑。(我们仍然必须根据经验中学到的经验法则来决定要测试什么。)(SSIS 通过自动实现多线程和管道、使用 BULK INSERT(即使在原始 SQL 语句中未指定)以及发送批处理来执行得更快在一个线程上插入,同时在其他线程上创建额外的 BULK INSERT。在这种情况下,它的执行速度大约是原始 SQL 语句的两倍。)当我过去教编程和 SQL Server 课程时,PowerBuilder 用户似乎有这样的声明“Native驱动程序提供最快的数据访问”刻在他们的舌头上,
@Nick“我完全反对它。最大的原因之一是earino 所说的第一个原因 - 它存在于一个地方。你不能很容易地将它集成到源代码控制中。让两个开发人员一起工作几乎是不可能的同时存储过程。”
并不是说我在争论将业务逻辑放在存储过程上(恰恰相反)。但是你提出的这些理由毫无意义。存储过程只是一个可以存储在源代码控制上的 sql/DDL 工件,它也是一个部署工件(即,移交给 dba 进行部署的东西,与您移交战争/耳朵的方式非常相似IT / 部署联络员的工件)一个或多个开发人员可以在同一个存储过程的源代码控制下工作,就像您对普通旧源代码所做的那样——通过分支、版本控制和合并。
现在,如果存储过程的唯一副本(以及包含它们的包)仅存在于数据库中,那么显然您无法使用源代码控制来控制它(以及与之相关的所有问题)。但是,这不是存储过程的问题,而是关于如何使用该代码的无能问题。这与在生产环境中只有一份源代码副本同样是一种无能的表现。
我曾在包含大量代码的系统中工作过,包括 Java 和 PLSQL/DDL,它们都在 clearcase 上进行了版本控制。它们都被视为源代码,将通过严格的流程进行编译和部署,并由不同的团队进行开发。从来没有像你描述的那样有任何问题。
有特定于上下文的原因不将业务逻辑放入存储过程中,但这些都不是有效的。
凭借所有这些微服务和微业务组件的意识形态,我们远远领先于质疑放置我们业务逻辑的正确位置。
即使这种意识形态被广泛接受,我们仍然有诱惑力,并且在某些情况下,我们最终将一些决策和业务逻辑放入数据库中。关于为什么不应该这样做的详细答案值得一游,但在宏观层面上,我建议考虑一个很好的理由,为什么它应该留在存储过程而不是应用程序层。
拥有这种反思想过程总是会指导做出正确的决定。在决定之前问这些问题:
- 如果我们将数据库从 SQL 更改为 Mongo 会怎样?
- 如果想要公开获取数据(该数据库提供)的 API 并在顶部应用此业务逻辑怎么办?
- 单元测试这个业务逻辑?
- 如果我们更改此业务逻辑的条件,涉及到 SDLC 步骤?
- 如果我们需要另一个用户输入来进行此业务逻辑中的决策怎么办?
- 如果它需要高计算能力(不是数据处理)并且我们希望在单独的进程(或平台)上运行怎么办?
在所有情况下,不将任何逻辑(除了数据检索)放在业务逻辑中都是自然的选择。
通过将逻辑移动到存储过程中,性能将大大提高,尤其是在涉及显式事务的情况下。
以我的经验,应用程序开发人员不太擅长编写优化的数据库代码,并且不倾向于考虑并发性或性能问题。
如果业务逻辑保留在应用程序层中,您往往必须通过网络转移大量数据(通常是多次往返),将其复制到数据库服务器的内存中,并且至少在应用程序服务器中复制一次,并在您打开交易时在应用程序中进行大量的逐行处理。然后应用程序开发人员抱怨数据库速度慢并且不断死锁。
如果您将逻辑放在任何可能的数据库中,您往往只是通过网络传递一些参数,在您等待网络资源时不会保留事务,整个事情就像涂了油的闪电一样。数据库当然应该像任何其他源代码一样进入源代码控制。有很多工具可以做到这一点。