本章介绍可用于持久性类的其他选项。
定义只读类可以定义一个持久性类,其对象可以打开但不能保存或删除。为此,请将类的READONLY参数指定为1:
Parameter READONLY = 1;
这仅在具有已映射到预先存在的存储的对象(例如现有全局globe或外部数据库)的情况下有用。如果在只读对象上调用%Save()方法,它将始终返回错误代码。
/// This sample persistent class represents a company.<br>
Class Sample.Company Extends (%Persistent, %Populate, %XML.Adaptor)
{
Parameter READONLY = 1;
}
添加索引
索引提供了一种优化跨持久类实例的搜索的机制;它们定义了与类相关联的,常用请求数据的特定排序子集。它们对于减少关键性能搜索的开销非常有帮助。
索引会自动跨越定义它们的类的整个范围。如果Person类具有子类Student,则Person中定义的所有索引都包含Person对象和Student对象。 Student类中定义的索引仅包含Student对象。
可以对属于其类的一个或多个属性进行排序。这可以对返回结果的顺序进行大量的特定控制。
此外,索引可以存储基于排序属性的查询经常请求的其他数据。通过将其他数据作为索引的一部分,可以极大地提高使用索引的查询的性能。当查询使用索引生成结果集时,无需访问主数据存储工具即可执行此操作。 (请参见下面的Data关键字。)
添加外键要在表之间强制引用完整性,可以在相应的持久性类中定义外键。修改包含外键约束的表时,将检查外键约束。
添加触发器由于CachéSQL支持使用触发器,因此与持久类相关联的任何触发器都将包含在该类的SQL映射中。
触发器是在CachéSQL中发生特定事件时执行的代码段。Caché支持基于INSERT,UPDATE和DELETE命令执行的触发器。根据触发定义,指定的代码将在相关命令执行之前或之后立即执行。每个事件可以有多个触发器,只要为其分配了执行顺序即可。
如果使用Foreach = row / object定义了触发器,则在对象访问期间的特定点也会调用该触发器。
存储类%CacheSQLStorage使用的持久性方法还会触发触发器,因为它在内部使用SQL语句来实现其持久性行为。
从CachéObjectScript引用字段在类定义中,可能会包含SQL中使用的CachéObjectScript代码的多个位置。例如,从SQL内部执行SQL计算的域代码和触发器代码。在这些情况下,没有当前对象的概念,因此无法使用点语法来访问或设置特定实例内的数据。
要引用当前行的特定字段,请使用{fieldname}语法,其中fieldname是该字段的名称。
例如,以下代码检查员工的薪水是否小于50000:
If {Salary} < 50000 {
// actions here...
}
注意:在UPDATE触发代码中,{fieldname}表示更新的字段值。在DELETE触发代码中,{fieldname}表示磁盘上字段的值。
要在SQL计算字段中引用当前字段,请使用{*}语法。
例如,以下代码可能会出现在“薪酬”字段的计算代码中,以根据“薪金”和“佣金”字段的值来计算其值:
Set {*} = {Salary} + {Commission}
Class User.MyClass Extends (%Persistent, %Populate) [ SqlTableName = My_Class ]
{
Property FirstName As %String(MAXLEN = 50);
Property LastName As %String(MAXLEN = 50);
Property FullName As %String [ SqlComputeCode = {set {*}={FirstName}_" "_{LastName}}, SqlComputed ];
}
添加行级安全性
除了其一般安全性外,Caché还提供了单行粒度的SQL安全性。这称为行级安全性。使用行级安全性,每行都包含一个授权查看者的列表,该查看者可以是用户或角色。
通常,通过向用户或角色授予对表或视图的SELECT特权来控制SQL安全性。当安全角色的数量大大少于用户数量时,角色的使用简化了访问控制。在大多数情况下,视图级安全性可以充分控制每个用户可以选择的行;但是,当实现所需控制所需的视图数量变得非常大时,就需要另一种替代方法来实现细粒度的访问控制。
例如,医院可以在线向每个患者提供患者特定的数据。为每个患者创建单独的视图不是可行的选择。取而代之的是,细粒度的访问控制与基于Caché角色的身份验证模型相结合,可以通过行级安全性高效,安全地创建此类应用程序。
以下是对行级安全性的使用限制:
- 行级安全性仅适用于持久性类。
- 行级安全性仅适用于在Caché服务器上实例化的表。它不适用于链接表(即在外部服务器上实例化的链接表)。
- 仅当从SQL访问行时才强制执行行级安全性。直接访问全局变量或通过对象接口访问全局变量时,不强制执行此操作。
设置行级安全性
要为表启用行级安全性,请编辑从中映射表的类的定义。此过程是:
-
在Studio中的“类Class”菜单上,选择“替代Override”。在出现的对话框中,转到“参数Parameters”选项卡,然后选择“ ROWLEVELSECURITY”参数。
-
在类定义代码中,将ROWLEVELSECURITY的值设置为1,例如:
ROWLEVELSECURITY = 1;
该参数的定义表示行级安全性处于活动状态,并且该类使用生成的%READERLIST属性存储有关对行具有授权访问权的用户和角色的信息。
另外,可以如下定义参数:
ROWLEVELSECURITY = rlsprop;
其中rlsprop是同一类中的属性的名称。
此替代方法表示行级安全性是活动的,并且该类使用给定的属性来存储有关对行具有授权访问权的用户和角色的信息。在这种情况下,还可以向该类添加索引,如下所示:
Index %RLI On rlsprop;
- 定义%SecurityPolicy()类方法,该方法确定并指定允许选择行的角色和用户名,并具有查看和表SELECT特权.
%SecurityPolicy()方法的结构为:
ClassMethod %SecurityPolicy() As %String [ SqlProc ]
{
QUIT ""
}
其特点是:
- 这是一个类方法,其名称为“%SecurityPolicy”。
- 它返回一个字符串(类型为%String)。
- 它需要零个或多个参数。如果此方法采用任何参数,则每个参数都必须与类中的属性名称匹配,并且它们都必须彼此区分。
- SqlProc关键字指定可以将该方法作为存储过程来调用。
- 该方法的QUIT语句返回可以查看该行的用户或角色。如果有多个用户或角色,QUIT必须返回用逗号分隔的名称列表。返回空字符串(如示例中所示)指定该行对拥有表的SELECT特权的所有用户可见。
重要说明:分配给%All角色的用户不会自动访问受行级安全性保护的表中的行。如果%All要访问这样的行,则%SecurityPolicy()方法必须明确指定此行。
- 编译该类和任何依赖类。
向具有现有数据的表添加行级安全性
要将行级安全性添加到具有现有数据的表中,请首先按照上一节“设置行级安全性”中所述的步骤进行操作。然后:
- 重建表的索引。
- 更新列出可以查看每一行的用户和角色的属性的值。
重建索引
警告:当用户访问该表的数据时,请勿重建索引。这样做可能会导致查询结果不准确。
重建表索引的过程是:
-
如果表中定义了任何具有WITH CHECK OPTION子句的视图,请使用DROP VIEW命令删除这些视图。(可以在更新谁有权访问每一行后重新创建这些视图)。
-
转到管理门户网站SQL页面 ([Home] > [SQL]),然后选择包含表的名称空间。
-
选择浏览SQL模式,该页面显示 [Home] > [SQL] > [Schemas] 页面。
-
在“Schemas ”页面上,在包含刚修改的表的包的行中,选择“Tables ”以显示其表页面。
-
在“Tables”页面上,在“Name ”列中选择表的名称以显示“Table Properties”页面。
-
在“Table Properties”页面上,选择“Rebuild Indices”以重建索引。
可以查看每一行的更新
- 从管理门户主页上,转到“执行SQL查询”页面 ([Home] > [SQL] > [Execute SQL Query])页面。
- 选择包含表的名称空间。
- 在“Execute SQL Query ”页面上的可编辑区域中,发出一条语句以更新表。它应具有以下形式:
UPDATE MySchema.MyClass SET rlsprop =
MySchema.SecurityPolicy(MySQLColumnName1, ...)
- MySchema是包含该类的架构(包)。
- MyClass是类的名称。
- rlsprop是包含可以读取该行的用户和角色列表的字段。默认情况下为%READERLIST,否则为ROWLEVELSECURITY参数的声明中指定的属性名称。
- SecurityPolicy是由%SecurityPolicy()方法的定义中的SqlName值指定的值。如果%SecurityPolicy()方法没有显式的SQL名称,并且其类为MySchema.MyClass,则其默认名称为myClass_sys_SecurityPolicy(具有MySchema.MyClass_sys_SecurityPolicy的完全限定形式)。
- MySQLColumnName1,…是与%SecurityPolicy()类方法中定义的参数(如果有)相对应的SQL列名称的集合。
- 单击执行查询。
- 如果需要,请重新创建最初删除的任何视图。
性能提示和信息
%READERLIST属性是一个计算字段,其值由%SecurityPolicy()方法确定。每当发生INSERT或UPDATE时,都会为该行调用%SecurityPolicy()并填充%READERLIST的值。
定义了%READERLIST属性上的集合索引,并且当启用行级安全性时,查询优化器可以利用该集合索引来最大程度地降低性能影响。
默认情况下,将ROWLEVELSECURITY设置为1时,将为%READERLIST属性(列)定义一个收集索引,因为安全策略通常可以返回多个逗号分隔的角色或用户名。如果安全策略永远不会返回一个以上的用户或角色名称,则可以覆盖ROWLEVELSECURITY参数,并将%RLI索引显式定义为普通(非集合)位图索引。通常,这可提供最佳性能。
安全提示和信息
使用行级安全性时,请记住以下安全性因素:
-
行级安全性除表级安全性外还起作用。要执行SELECT,INSERT,UPDATE或DELETE语句,必须为用户授予相关行的表级访问和行级访问权限。
-
在尝试运行SQL命令时,会在运行时动态检查用户特权。
-
如果创建可更新视图并指定WITH CHECK OPTION,则对该视图的INSERT操作将检查要插入的行是否通过了该视图中指定的WHERE子句。此外,如果要从具有行级安全性的表创建视图,则如果要发出的视图的WHERE语句或隐式行级安全性谓词导致该行不可见,则INSERT失败视图上的SELECT * FROM命令。
-
如果有权访问某行,则可以更改%READERLIST字段的值(或包含可查看该行的用户和角色的列表的任何字段)。这意味着可以执行直接或间接删除对行的访问的操作。
-
如果在未定义行级安全性的情况下尝试插入将违反UNIQUE约束的行,则在定义行级安全性的情况下仍然会违反约束,无论导致约束失败的行是否可见到更新的事务。