从 SQL 到 SOQL





5.00/5 (1投票)
从 .NET/SQL 转向 Force.com/SOQL,软件开发人员会面临哪些差异?要完成这种转变,你需要了解什么?
引言
[相关:注册您的 免费 Force.com 开发者版环境!]
对于 .NET 开发者来说,转向 Force.com 会发现许多不同之处,因为 Force.com 代表了一种截然不同的软件开发模式。它有开发体验,依赖于对底层平台的高度抽象,而不是 Visual Studio 的设计界面和代码生成。它有 Apex 语言,虽然表面上看起来像 C#,但需要一套完全不同的设计模式。然后是数据库。
对于 .NET 开发者来说,“数据库”几乎总是指 SQL Server 或其他使用 SQL 变体的脱机数据库。而对于 Force.com 开发者来说,“数据库”几乎总是指集成的 Force.com 数据库,它使用一种称为 SOQL 的查询语言和一种称为 SOSL 的搜索语言。
在比较数据库时,人们往往倾向于比较数据库功能或查询语言功能。如果您是数据库专家,欢迎您这样做。但大多数 .NET 开发者并不是数据库“专家”——我们是数据库用户。我们编写旨在执行数据库操作的解决方案,我们的目标通常是尽可能有效和高效地完成这些操作,同时希望避免可能影响可伸缩性或安全性的可怕错误。在本文中,我将不讨论数据库功能,而更多地讨论开发流程。换句话说——从软件开发人员的角度来看,.NET/SQL 与 Force.com/SOQL 之间有什么区别?要完成这种转变,你需要了解什么?
集成的乐趣
.NET 框架设计为数据库无关——可以与任何数据库协同工作,尽管绝大多数 .NET 开发者使用 SQL Server 的一种变体。虽然您可以使用集成技术将数据存储在 Force.com 外部,但绝大多数 Force.com 应用程序都使用集成数据库。数据库集成这一事实对软件开发产生了深远的影响。
在 .NET 中,数据库访问涉及各种类和组件,例如数据集、数据适配器、命令、连接和绑定类。这些类或相关的 LINQ to SQL 类提供了开发人员访问数据库的机制。它们必须足够灵活,能够处理各种数据库提供程序和查询语言。Force.com 数据库的集成特性消除了对所有这些组件和类的需求——因为您只需要与一个数据库进行本机连接,就可以设计编程模型来直接访问数据库。
在 .NET 中,存储连接字符串是一个挑战。将连接字符串存储在 web.config 文件中是一种常见的做法。虽然您可以使用加密来保护连接字符串,但许多开发人员并不这样做。无论哪种情况,任何能够访问服务器的人都可以检索连接字符串并访问数据库。在 Force.com 中,没有连接字符串——数据库访问安全性是平台访问的固有组成部分。
数据库安全始于保护连接字符串。使用 .NET,数据库可能与 Windows 集成安全,也可能不集成。即使集成了,其安全配置也与 Windows 不同。您需要关注多层面的安全——配置数据库服务器的能力、创建或修改数据库、创建或修改数据库表以及在这些表中读写数据的能力。您需要确定、管理,有时还需要实现您的身份验证方案。Force.com 上的数据库安全也很复杂,但方式不同。身份验证很简单,尤其是如果您完全依赖内置身份验证。配置数据库和修改表的能力与访问 Force.com 平台的相同用户标识绑定。数据访问通常更复杂,可以根据用户配置文件保护单个字段,并根据用户、角色或可配置的数据共享规则控制对数据库中单个记录的访问。
在 .NET 中,您还必须关注数据库部署。如果您管理自己的数据库,它必须位于某个服务器上。该服务器需要维护、保护并定期升级。如果您使用托管服务或云服务,您需要管理服务帐户并实现和配置到数据库的连接。在 Force.com 中,没有部署问题——安全性、访问权限、升级以及数据维护和完整性问题都由 Salesforce 处理。除了安全性——这需要学习一种与 .NET 中使用的数据库安全模型截然不同的模型外,Force.com 数据库的集成特性极大地简化了数据库连接、访问和维护任务。它还影响您对数据库的编程方式。
实体,无需框架
任何使用任何语言数据库的开发者面临的一个基本挑战是将编程概念映射到数据库概念。作为开发者,我们习惯于使用面向对象编程——数据被抽象为可以具有字段和其他对象引用的对象。但数据库使用具有数据类型的表,这些数据类型可能无法很好地映射到您正在使用的语言,并且虽然表具有查找关系;但它们的底层机制与软件中使用的不同。在 .NET 中,您可以手动将数据库数据映射到对象,或者使用强类型数据集或 Entity Framework 等工具和框架。
您可以将 Force.com 视为 Entity Framework,但没有框架。在 Force.com 中,每个数据库表都对应一个继承自 SObject 对象类型的对象。数据库字段自动映射到其对应的语言类型。创建对象关系映射不需要工具、配置或 XML 文件——它会自动发生。如果您向数据库对象添加一个字段,该字段可以立即从工作流和代码中引用。更重要的是,一旦您引用了一个数据库字段,Force.com 就会阻止您删除该字段或将其更改为不兼容的数据类型。
对象和表之间的关系如此紧密,以至于在 Force.com 中我们根本不提表——我们只称它们为对象(在代码讨论中称为 SObject)。
Force.com 数据库的本机语言是 SOQL(Salesforce 对象查询语言)。一种称为 SOSL(Salesforce 对象搜索语言)的独立语言用于搜索——它能够在一个操作中跨多个字段和对象类型搜索文本值。
与 SQL 不同,SOQL 仅用于查询数据。插入、更新和删除操作是通过一组称为 DML(数据操作语言)方法的独立方法执行的。要插入一个对象,您可以使用 Apex 的 new 语句创建一个该对象实例,然后使用 insert 方法将其插入。例如:这是插入单个联系人对象的方法
// DML operation to insert a single contact Contact c = new Contact(testfield__c = 'initialvalue', FirstName = 'Joe', LastName = 'Jones'); insert c;
要更新对象,您可以使用 SOQL 查询语句加载对象的当前值,修改您希望更改的任何字段,然后使用 update 方法将更改推送到数据库。当然,这些 DML 操作可以作用于对象数组,就像数据集更新可以在 .NET 的单个更新操作中修改多行一样。
SOQL
SOQL 语句有两种类型——静态 SOQL 和动态 SOQL。静态 SOQL 语句在精神上有些像 LINQ,但语法不同。这是一个查询“Contact”表(我将在此后称其为联系人对象)的示例。
String searchfor = 'Jones'; Contact[] contacts = [Select testfield__c, FirstName, LastName from Contact where LastName= :searchfor];
第一行定义了一个字符串变量“searchfor
”,其中包含我们要查找的人的姓氏。第二行定义了一个名为“contacts”的数组,其中加载了数据库中所有姓氏为“Jones”的联系人对象。方括号将 SOQL 代码与 Apex 语言代码分开。冒号“:”符号转义 SOQL 语句,以便将变量的值包含在查询中。
SOQL 语句会被编译。这意味着会进行大量的编译时检查。编译器将验证所有请求的字段是否存在于数据库中。它还将阻止任何人从数据库中删除任何这些字段,直到它们首先从代码中删除。
动态 SOQL 用于需要在运行时定义查询。此语句的动态等效语句如下所示
Contact[] contacts = (Contact[])Database.query('Select testfield__c, FirstName, LastName from Contact where LastName= :searchfor');
静态 SOQL 几乎总是优于动态 SOQL,因为动态 SOQL 缺乏编译时字段检查和免受字段删除的保护——平台无法预料您会在动态字符串中包含哪些字段。但是,在某些情况下,首选动态 SOQL,特别是在创建打算出售给多个组织的软件程序包时。
连接简易化
您是否曾对左内连接、右外连接以及所有其他 SQL 连接语法感到困惑?我得说实话,这对我来说从来不是自然而然的。在 Force.com 上,对象关系会以一种……几乎神奇的方式发生。例如,标准的 Contact 对象可以与 Account 对象关联——这是一种一对多关系,每个联系人包含一个指向一个账户的查找,并且每个账户都可以被多个联系人引用。
假设您想查询上例中联系人的账户名称(如果有)。在 SQL 中,您必须使用 Join 语句指定查找关系。在 SOQL 中,您可以简单地引用账户上的字段,如下所示,使用“Account.Name
”
Contact[] contacts = [Select ID, Account.Name, Name, Email from Contact where LastName = :searchfor];
如果联系人没有属于某个账户,则账户名称将为 null。并且可以轻松地在代码中引用检索到的第一个联系人的账户名称,如下所示
String theaccountname = contacts[0].Account.Name;
反之呢?如果您想获取账户名称为“XYZ Inc.”的所有联系人的姓名,该怎么做?
Account[] accounts = [Select ID, Name, (Select ID, LastName from Contacts) from Account where Name = 'XYZ Inc.'];
子查询为您构建了对象关系,其中每个账户都引用了一个关联联系人数组。然后,您可以像这样检索第一个账户的第二个联系人的姓氏
String thefirstcontact = accounts[0].contacts[0].LastName;
当然,在实际程序中,您会先检查以确保找到了一个对象,方法是测试账户数组的长度以及该账户的联系人数组的长度。
正如您所见,Force.com 的对象关系映射功能不仅限于简单的对象检索,还包括对象关系。
数据库功能
在 SQL 中,您可能会使用存储过程和索引来提高性能。Force.com 在内部使用这些概念,但它们在很大程度上对您隐藏了。您创建所需的查询,并依赖平台优化您的请求到底层数据库。
由于没有存储过程,DML 操作已移入语言,并且数据库管理已内置到平台中,因此 SOQL 比 SQL 更简单,因为它仅用于查询——相当于 SQL 的 SELECT 语句。因此,您会发现使用它与 SQL 类似,但有一些有趣的转折。
- 您已经看到,无需 JOIN 语句——对象关系是隐含的。
- SOQL 支持多态关系。Force.com 有时允许对象字段引用多种类型的对象。多态关系允许您根据所引用对象的类型指定不同的字段进行查询。
- 聚合函数。SOQL 支持与 SQL 类似的聚合函数,如
SUM()
和Count()
。它还支持排序和分组。 - 日期范围查询。SOQL 内置了对财政季度和财政年度的支持。它可以做到这一点,因为它了解运行当前应用程序的组织的财政年度设置。
- 货币字段。SQL 和 SOQL 都包含“货币”类型。然而,SOQL 同时了解组织的参考货币和当前用户的参考货币。这使得 SOQL 能够为使用多种货币的组织执行货币转换。
- SOQL 支持乐观和显式记录锁定。
总的来说,您会发现 SOQL 与 SQL 非常相似,只是更易于使用。
事务
Force.com 数据库同时支持自动和显式事务。
自动事务值得特别注意。假设您插入了一个对象,并在该对象类型的插入触发器中实现了修改其他记录的代码。在 SQL 中,如果发生异常,您需要显式使用事务边界来确保数据完整性得到维护。在 Force.com 中,整个插入操作默认封装在事务中——如果在插入过程中执行的任何代码中发生错误,插入尝试期间的所有数据库操作都将回滚。
但也许最有用的自动事务是单元测试期间发生的事务。您的单元测试可以随意创建和修改数据——所有更改将在测试结束时回滚。实际上,默认情况下,您的单元测试会看到一个基本为空的数据库,这使得创建不受数据库当前状态影响的单元测试变得容易。这甚至使得对生产系统进行安全测试和诊断成为可能——您的单元测试不会以任何方式影响生产数据。
结论
有些人认为 Force.com 是一个可编程数据库,而不是通用的软件开发平台。这是错误的。首先,因为 Force.com 本身就是数据库的用户——在底层深处,它们正在使用其他数据库技术。SOQL 实际上并不是一种原生的数据库语言——您编写的 SOQL 最终会被编译成原生的数据库查询。因此,认为 Force.com 是一个可编程数据库在技术上是有疑问的。
但更重要的是,将 Force.com 视为一个可编程数据库过于局限——该平台可用于创建数据库是附带的,或者根本不使用的应用程序。话又说回来,数据库无缝集成的特性使得它易于使用,您可能会发现自己将其融入到您以前未曾考虑过的设计中。
[相关:注册您的 免费 Force.com 开发者版环境!]
本文最初发表于 Salesforce 开发者网络,2013 年 3 月