草庐IT

mysql - 防止SQL蠕变的最佳做法是什么?

coder 2023-10-08 原文

我有一个用php编写的webapp,它使用mysql数据库后端。这个问题同样容易适用于任何试图使用sql数据库和mvc oop设计的语言和应用程序。
如何将sql代码限制在模型中?
在这个问题的背后,有一个相当长的故事专门针对我的案例。如前所述,我正在一个php/mysql/ajax网站上工作。我使用面向对象和mvc的设计原则来设计它,包括模型、视图和控制器。我设法将视图元素(如标记和样式)完全限制在视图中,并使它们相当容易重用。我想我对sql代码也做了同样的处理。但是随着工作的进展,很明显模型需要一些认真的重构。
我发现将sql保存在模型中的方法是将每个sql查询封装在自己的查询对象中。然后当我需要在视图或控制器中调用一些sql时,我将通过工厂访问查询。控制器或视图中不存在SQL代码。
但这变得异常乏味。我不认为这样做实际上会有什么收获,我花了太多时间创建名为“selectidsfromtablewhereuser”的查询。查询工厂正在接近数千条生产线。在eclipse中进行的一点搜索显示,绝大多数查询都在一两个地方使用,以后再也不会使用了。不好的。
我知道在一个好的mvc中,您需要将sql与控制器或视图完全分离。但在这一点上,在我看来,最好是把sql放在代码中需要它的地方,而不要费心把它和数据库代码埋在模型的深处。这些查询只使用一次,为什么还要封装它们呢?
将sql与controller或view分开有那么重要吗?这样做有什么好处?让它扩散会失去什么?你怎么解决这个问题?
根据请求编辑,这里有关于我的模型的更多细节。
它有两部分。表部分和查询部分。tables部分包含域对象——主要设计为类对象的包装器,类对象与数据库中的表完全类似。例如,可能有一个数据库表Foo包含字段idnametype。将有一个表对象(class FooTable)具有一个字段为“id”、“name”和“type”的数组。看起来是这样的:

class FooTable extends MySQLTable {
    private $id;
    private $data;
    private $statements;

    public function __construct($id, $data=NULL, $populate=false) {
         // Initialize the table with prepared statements to populate, update and insert.  Also,
         // initialize it with any data passed in from the $data object.
    }

    public function get($field) {}
    public function set($field, $value) {}
    public function populate() {}
    public function update() {}
    public function insert() {}
}

如果有一个fooBar数据库表与字段FooBarsid有一对多的关系(onefooIDmanybar),那么就会有一个FooBar表对象(class FooBarTable),它看起来与上面的FooTable几乎完全相同。
FooTable和许多FooBarTable对象都将包含在Foo对象中。给Foo对象工厂一个id到一个Foo表,它用Foo的数据和它的所有Bar及其数据填充自己。
查询对象用于按照需要的顺序拉出那些Fooid。因此,如果我想让Foo对象按日期、投票或名称排序,我需要一个不同的查询对象来完成这项工作。或者如果我想选择在一定范围内具有Foo的所有Bar对象。我需要一个查询对象。
大多数时候,我使用表对象(包装器,而不是基表)与数据库交互。但是在选择哪些表对象时,这就是查询的来源。
在最初的设计中,我不认为会有太多的查询,我认为它们会被重用。因为可能有几个地方我希望Foo按日期顺序排列。但事情并不是这样的。它们的数量远远超过预期,而且大多数都是一次性的,在某些视图或命令中使用一次,然后再也不会使用。我还认为,这些查询可能封装了相当复杂的sql,最好将它们作为对象,这样我就可以始终确保为它们提供所需的数据,而且这将是一个相对干净的环境,可以在其中测试sql查询本身。但同样,事情并不是这样的。其中大多数包含非常简单的sql。

最佳答案

从最后一个问题开始:所得到的是关注点的分离,或者用通俗的英语来说就是“把属于彼此的东西放在一起”。这里的关键词是归属,这是一个相当主观的词。
在早期的php中,很多人发现,一个页面上的任何内容都“属于”在一起。因为php曾经是“个人主页”的缩写,所以它的设计目标是有几个页面的小站点,然后这种归属感就完全有意义了。
当事情发展的时候,归属感就会改变。当我们得到一个复杂的数据模型,它需要保持一致性,并且必须随着时间的推移而发展,然后突然之间,这个“模型”上的操作开始“归属”在一起,因为很难在所有地方挖掘出操作和sql查询。所以为了保持控制,我们需要所有的模型操作都在同一个页面上。
作为一种实用的方法,我喜欢在ui和模型之间的沙地上画一条线,并在那里定义一个api,它封装了小用户的目标(用户希望看到promotions-->需要getpromotions方法,用户希望向cart添加一些东西:需要addToCart方法等等)。
我喜欢在这里画一条线,因为它捕捉用户的需求,ui的职责是让用户以一种简单有效的方式到达那里,服务/模型/存储库层的职责是实现这个需求。
如果操作正确,它也在从用户删除的级别1步骤上。然而,许多开发人员混淆了用户想要什么和需要实现的特性。然后你会得到非常无效的服务方法,比如saveproject(用户不希望项目被保存,她只希望下次登录时项目就在那里)。它被认为是理所当然的,因此在服务api中没有位置)。这种基于实现的api导致了几乎为空的包装器方法层。
像存储库之类的东西,是构建这个服务层的一种方法。
这种面向用户目标的api方法还倾向于清理视图(显示元素的内容可以通过少量的服务调用获取)和控制器(然后一个操作由正常的清理和通常是一个方法调用组成)。

关于mysql - 防止SQL蠕变的最佳做法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3775416/

有关mysql - 防止SQL蠕变的最佳做法是什么?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  3. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐