第二十章 Caché 定义回调方法

文章目录

  • 第二十章 Caché 定义回调方法
  • 简介
  • 回调和触发器
  • %OnAddToSaveSet()
  • %OnAfterDelete()
  • %OnAfterSave()
  • %OnBeforeSave()
  • %OnClose()
  • %OnConstructClone()
  • %OnDelete()
  • %OnNew()
  • %OnOpen()
  • %OnReload
  • %OnRollBack()
  • %OnValidateObject()
  • %OnDetermineClass()
    • 回调 %OnDetermineClass()
    • 对%OnDetermineClass()的调用结果的示例

第二十章 Caché 定义回调方法

简介

回调方法由系统方法调用,以允许其他用户提供的处理。可以通过使用以“%On”或“ On”开头的名称来识别回调方法,通常后跟调用它们的方法的名称。在大多数情况下,这是完整名称,例如%OnNew()。

要实现回调方法,请使用Studio重写继承的方法或使用正确的签名定义一个新方法。这些技术是等效的。要重写此方法,请使用CachéStudio中的“覆盖”对话框。要访问此对话框,请单击Class —> Override。

如果系统方法具有已实现的回调方法,则当系统方法运行时,该方法将调用该回调方法。例如,如果实现了%OnDelete(),则%Delete()调用%OnDelete()。

重要:不要显式执行回调方法。

回调名称 继承类 方法类型 是否私有方法
%OnAddToSaveSet() %RegisteredObject Instance Yes
%OnAfterDelete() %Persistent Class Yes
%OnAfterSave() %Persistent Instance Yes
%OnBeforeSave() %Persistent Instance Yes
%OnClose() %RegisteredObject Instance Yes
%OnConstructClone() %RegisteredObject Instance Yes
%OnDelete() %Persistent Class Yes
%OnNew() %RegisteredObject Instance Yes
%OnOpen() %Persistent, %SerialObject Instance Yes
%OnReload %Persistent Instance Yes
%OnRollBack %Persistent Instance Yes
%OnValidateObject() %RegisteredObject Instance Yes
%OnDetermineClass() %CacheStorage Class No

注意:对于所有作为私有方法的回调,如果选中了“类引用”右上角的“私有”复选框,则仅在“类引用”中可见它们的文档。

回调和触发器

对于同时使用SQL和对象访问的应用程序,如果实现触发器,通常希望在对象访问的等效点调用相同的逻辑。例如,如果在删除行时插入审核记录,则在删除对象时也应该插入审核记录。

如果使用Foreach = row / object定义了触发器,则在对象访问期间的特定点也会调用该触发器。

但是,如果无法创建此类触发器,并且希望按照前面所述的方式同步SQL和对象行为,则必须实现一个或多个回调。在这些实现中,使用与触发器定义中使用的逻辑等效的逻辑。请注意,以下回调方法具有与SQL触发器相同的功能:

  • %OnBeforeSave() — 插入之前,更新之前
  • %OnAfterSave() — 插入后,更新后
  • %OnDelete() — 删除前

%OnAddToSaveSet()

每当通过%AddToSaveSet()将当前对象添加到SaveSet时,都会调用此实例方法。%AddToSaveSet()可以通过以下方式调用:

  • %Save() %Persistent的实例
  • %GetSwizzleObject() %SerialObject的实例
  • %AddToSaveSet() 用于引用对象

如果%OnAddToSaveSet()修改了另一个对象,则%OnAddToSaveSet()负责在该修改后的对象上调用%AddToSaveSet()。从%OnAddToSaveSet()调用%AddToSaveSet()时,将depth 作为第一个参数传递,并将1作为第二个参数传递。

当在名为MyPerson的对象上调用%Save()时,Caché会生成MyPerson引用的对象列表。SaveSet是由要保存的对象及其引用的所有对象组成的对象列表。在示例中,SaveSet可能包含引用的对象MySpouse,MyDoctor等。

%OnAddToSaveSet()示例:

ethod %OnAddToSaveSet(depth As %Integer,
                       insert As %Integer,
                       callcount As %Integer)
                        As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

其中:

  • depth 从%AddToSaveSet()传入的整数值,表示SaveSet构造的内部状态。如果使用%OnAddToSaveSet()将其他任何对象添加到SaveSet,请将此值传递给%AddToSaveSet()而不进行更改。

  • insert 一个标志,指示是否将要保存的对象插入扩展区(1)或它已经是扩展区(0)的一部分。

  • callcount 已为此对象调用%OnAddToSaveSet的次数。,%AddToSaveSet可以在同一对象上多次调用。

该方法返回%Status代码,其中失败状态导致保存失败并回滚事务。

可以通过调用%AddToSaveSet()来更新对象,创建新对象,删除对象并要求对象将其自身包含在当前SaveSet中。如果修改当前实例或其任何后代,则必须让系统知道已完成此操作。为此,请为修改后的实例调用%AddToSaveSet()并将Refresh参数指定为1。

如果使用%OnAddToSaveSet()删除对象,请确保调用%RemoveFromSaveSet()来清除对该对象的任何引用。

/// d ##class(PHA.OP.MOB.Test).AddMyClass()
ClassMethod AddMyClass(WebSocketID)
{
	s one=##class(User.MyClass).%New()
	s one.PropertyName="1"
	s one.PropertyName1="7"
	s one.FirstName="Yao"
	s one.LastName="Xin"
	s one.Status=""
	s one.Multis("11")=11
	s one.Multis("aa")="aa"
	s one.Multis("Y")="X"
	s one.MaxLen10="abcdefghiklmnopq"
	s one.Flag="Y"
	s sc=one.%Save()
	w sc
}

Class User.MyClass Extends (%Persistent, %Populate) [ SqlTableName = My_Class ]
{
Method %OnAddToSaveSet(depth As %Integer = 3, insert As %Integer = 0, callcount As %Integer = 0) As %Status [ Private, ServerOnly = 1 ]
{
	w "Save()回调%OnAddToSaveSet,!"
	Quit $$$OK
}

}
DHC-APP>d ##class(PHA.OP.MOB.Test).AddMyClass()
Save()回调%OnAddToSaveSet,!1

第二十章 Caché 定义回调方法_第1张图片

%OnAfterDelete()

删除指定对象后(立即成功调用%DeleteData()之后),%Delete()方法将调用此类方法。使用此方法,可以在要保存的对象范围之外执行操作,例如排队稍后的通知操作。

ClassMethod %OnAfterDelete(oid As %ObjectIdentity) As %Status [ Private, ServerOnly = 1 ]  
{
    // body of method here...
}
  • oid 被删除的对象。

该方法返回%Status代码,其中故障状态导致%Delete()失败,如果存在活动事务,则将其回滚。如果%Delete()返回一个错误(它自己的错误或一个源于%DeleteData()的错误),则没有对%OnAfterDelete()的调用。

%OnAfterSave()

保存对象后,%Save()方法将调用此实例方法。使用此方法,可以在要保存的对象范围之外执行操作,例如排队稍后的通知操作。例如,一家银行使用超过一定金额的存款来使客户向其发送有关存款政策的说明。

Method %OnAfterSave(insert as %Boolean)As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}
  • insert 一个标志,指示是否将要保存的对象插入到扩展区(1)或它是对现有对象的更新(0)。

该方法返回%Status代码,其中失败状态导致%Save()失败并最终回滚事务。

%OnBeforeSave()

在保存对象之前,%Save()方法将调用此实例方法。使用此方法,可以在完成操作之前请求用户确认,然后再将实例保存到磁盘。

重要说明:在%OnBeforeSave()中修改当前对象无效。如果要在保存对象之前修改对象,请改用%OnAddToSaveSet()回调,并将逻辑包含在该方法中。

Method %OnBeforeSave(insert as %Boolean) As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}
  • insert 一个标志,指示是否将要保存的对象插入到扩展区(1)或它是对现有对象的更新(0)。
  • 该方法返回%Status代码,其中失败状态导致保存失败。
Method %OnAddToSaveSet(depth As %Integer = 3, insert As %Integer = 0, callcount As %Integer = 0) As %Status [ Private, ServerOnly = 1 ]
{
	w "Save()回调%OnAddToSaveSet,!",!
	Quit $$$OK
}

Method %OnAfterSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]
{
	w "Save()回调%OnAfterSave,!",!
	Quit $$$OK
}

Method %OnBeforeSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]
{
	w "Save()回调%OnBeforeSave,!",!
	Quit $$$OK
}
DHC-APP> d ##class(PHA.OP.MOB.Test).AddMyClass(8)
Save()回调%OnAddToSaveSet,!
Save()回调%OnBeforeSave,!
Save()回调%OnAfterSave,!
1

%OnClose()

该实例方法在对象被破坏之前立即被调用,从而为用户提供了对任何辅助项目执行操作的机会,例如释放锁或删除临时数据结构。

Method %OnClose() As %Status [ Private, ServerOnly = 1 ]
{
    // body of method here...
}

该方法返回%Status代码,其中故障状态仅是信息性的,无任何作用以防止对象被破坏。

%OnConstructClone()

在为克隆对象分配了结构并将所有数据复制到其中之后,%ConstructClone()方法立即调用此实例方法。该方法允许执行与克隆的对象有关的任何其他操作,例如取出锁或重置克隆的任何属性值。

Method %OnConstructClone(object As %RegisteredObject,
                        deep As %Boolean,
                        ByRef cloned As %String)
                        As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}
  • object 被克隆的对象
  • deep 克隆过程有多“deep”,其中0指定克隆指向与原始对象相同的相关对象; 1使与要克隆的对象相关的对象也被克隆,以便克隆获得自己的一组相关对象。
  • cloned 一个参数的用法根据%OnConstructClone()的调用方式而异。

该方法返回%Status代码,其中故障状态阻止创建克隆。

%OnDelete()

在删除对象之前,%Delete()方法将调用此类方法。此方法可用于确保删除对象不会破坏数据完整性,例如通过确保仅在包含空对象的情况下才删除旨在包含其他对象的对象。

ClassMethod %OnDelete(oid As %ObjectIdentity)As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}
  • oid 被删除对象的对象标识符。

该方法返回%Status代码,其中发生错误停止删除。

%OnNew()

当分配了对象的内存并初始化属性时,%New()方法将调用此实例方法。

Method %OnNew(initvalue As %String)As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}
  • initvalue 该方法用于设置对象的字符串,除非被重写,否则将在下一条说明中进行描述。

重要说明:%OnNew()的参数必须与%New()的参数匹配。自定义此方法时,请使用希望从%New()接收到的任何变量和类型来覆盖参数。例如,如果%New()接受两个参数-dob表示出生日期,name表示名字和姓氏,则%OnNew()的写法可能是:

Method %OnNew(dob as %Date = "", name as %Name = "") as %Status [ Private, ServerOnly = 1 ] 
{
 // body of method here...
}

该方法返回%Status代码,其中发生错误停止创建对象。

例如,对于一个实例必须为其Name属性具有值的类,回调可能具有以下形式:

Method %OnNew(initvalue As %String) As %Status
{
    If initvalue="" Quit $$$ERROR($$$GeneralError,"Must supply a name")
    Set ..Name=initvalue
    Quit $$$OK
}

%OnOpen()

在打开对象之前,%Open()方法将调用此实例方法。与任何相关实体相比,它允许验证实例的状态。

Method %OnOpen() As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

该方法返回%Status代码,其中发生错误停止对象的打开。

%OnReload

%Reload方法调用此实例方法,以提供有关oid指定的对象已重新加载的通知。请注意,当oid标识的对象已经在内存中时,%Open会调用%Reload。如果此方法返回错误,则不会打开该对象。

Method %OnReload() 
    As %Status [ Private, ServerOnly = 1 ]
  {
    // body of method here...
  }

该方法返回%Status代码,其中发生错误停止回滚操作。

%OnRollBack()

当Caché回滚之前已成功序列化为SaveSet一部分的对象时,它将调用此实例方法。

当为持久对象或流调用%Save()或为序列对象调用%GetSwizzleObject()时,Caché将启动保存事务,该事务包含SaveSet中的所有对象。如果%Save()失败(例如,由于属性未通过验证),则Caché会回滚先前已成功序列化为SaveSet一部分的所有对象。也就是说,对于每个对象,Caché都会调用%RollBack(),后者将调用%OnRollBack()。对于尚未成功序列化的对象(即无效的对象),Caché不会调用此方法。

%RollBack()将该对象的磁盘上数据状态恢复为其事务处理前的状态,但是除了其ID分配以外,不影响已设置该对象的任何属性的内存中状态。 如果要还原内存中的更改,请在%OnRollBack()中进行。

Method %OnRollBack() As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

该方法返回%Status代码,其中发生错误停止回滚操作。

%OnValidateObject()

发生所有验证后,%ValidateObject()方法将调用此实例方法。可以执行自定义验证,例如,一个属性的有效值根据另一属性的值而变化。

Method %OnValidateObject() As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

该方法返回%Status代码,其中失败状态导致验证失败。

%OnDetermineClass()

%OnDetermineClass()类方法返回该对象的最specific 类型的类。%OnDetermineClass()由默认存储类实现。如果使用自定义存储或SQL存储,则此方法没有默认实现,但可以实现。

ClassMethod %OnDetermineClass(
        oid As %ObjectIdentity, 
        ByRef class As %String) 
    As %Status [ ServerOnly = 1 ]
  • oid 对象的对象标识。
  • class 是oid标识的实例的最specific 的类型类。该对象不是该类的任何子类的实例。

返回值是指示成功或失败的状态值。

回调 %OnDetermineClass()

%OnDetermineClass()可以通过以下两种方式之一调用:

Set status = ##class(APackage.AClass).%OnDetermineClass(myoid, .myclass)
Set status = myinstance.%OnDetermineClass(myoid, .myclass)
  • myoid 是要确定其最specific类型类的对象,而myclass是已标识的类。Package.AClass是从中调用方法的类,myinstance是从中调用方法的实例。

在这种情况下,该方法将为myoid计算最specific的类型类,并将myclass设置为等于该值。如果myoid不是当前类的实例,则返回错误。

考虑将%OnDetermineClass()与Sample.Employee一起使用的示例,该示例是Sample.Person的子类。

Set status = ##class(Sample.Employee).%OnDetermineClass(myoid, .class)

而myoid指向最specific类型为Sample.Person的对象,则调用将返回错误。

对%OnDetermineClass()的调用结果的示例

假设有一个MyPackage.GradStudent类扩展了MyPackage.Student类,该类扩展了MyPackage.Person类。下面显示了调用%OnDetermineClass()的结果,传入了最特定类型为MyPackage.Student的对象的OID:

  • ##class(MyPackage.Person).%OnDetermineClass(myOid,.myClass)
    • Return value: $$$OK
    • myClass set to: MyPackage.Student
  • ##class(MyPackage.Student).%OnDetermineClass(myOid,.myClass)
    • Return value: $$$OK
    • myClass set to: MyPackage.Student
  • ##class(MyPackage.GradStudent).%OnDetermineClass(myOid,.myClass)
    • Return value: error status
    • myClass set to: “”

你可能感兴趣的:(Caché,从入门到精通)