|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Пытаюсь сделать запись изменений в объектах в триггерах. Для этого написал один базовый класс, от которого наследуются другие %Persistent-классы. Такой вот триггер: Trigger LogEventBeforeSave [ Event = UPDATE ] { set newHistory = ##class(Common.HistoryObject).%New() set newHistory.DateChange = $p(dt,",",1) set newHistory.TimeChange = $p(dt,",",2) set newHistory.Username = $username set newHistory.TargetClassName = ..%ClassName(1) set newHistory.TargetId = {ID} set CI = ##class(%Dictionary.CompiledClass).%OpenId(..%ClassName(1)) for i=1:1:CI.Properties.Count() { if (CI.Properties.GetAt(i).Private = 1) { continue } set prop = CI.Properties.GetAt(i) // Дальше нужно получить новое значение prop } } Проблема в том, что непонятно, как получить значение свойства. В документации, конечно, написано, мол, юзайте фигурные скобки: {FieldName1}, но так как этот триггер будет общим для производных классов, то неизвестно, как будут называться поля. Может быть кто-нибудь знает, как получить в триггере поля и значения, изменяемые SQL-запросом? ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 04:05 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Методы-генераторы вам нужно использовать. Но это не слишком просто, даже лень писать :-) ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 06:00 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Да вот тоже думаю про генераторы, но вот как в такую функцию передать из триггера изменённые поля, не пойму... Айдишник можно передать, класснейм тоже, но сами поля, тем более неизвестно заранее, какие они будут... В самой функции нельзя выполнить что-то вроде Do %code.WriteLine(" write {ID}"), будет ругаться на {ID}. Короче, непонятно пока что. ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 06:39 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Vixler, Зная название свойства (в вашем случае через prop.Name) можно сделать такую конструкцию: w $property(newHistory, prop.Name) через $property так же можно присваивать данные ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 06:40 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
D_De1mos, Не, newHistory - это просто объект для записи изменений, он не является базовым классом для других, которые нужно журналировать. То есть $property(newHistory, prop.Name) возвращает значение поля prop.Name у объекта newHistory, а нужно $property(##this, prop.Name), только это не будет работать, потому что Trigger по сути является ClassMethod'ом и ##this не будет иметь значения. Но в то же время в Trigger передаются значения изменённых полей через {ID},{Prop1},{Prop2}, только Prop1 и Prop2 - это имена полей в разбираемом классе, а так как триггер наследуемый, то неизвестно, какими эти поля будут. Нужен какой-то общий механизм, наверное должен быть какой-то список значений с именами полей, передаваемый в триггер, но найти его не могу... ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 07:15 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
/// Добавление в лог /// aOperType - Тип операции (1 добавление, 0 изменение, -1 удаление) ClassMethod ChangeLog(aOperType As %Integer, aID As %String) [ CodeMode = objectgenerator ] { q:%class.ClassType'="persistent" 1 d %code.WriteLine(" s cNameClass="""_%class.Name_"""") d %code.WriteLine(" s cUserName=$USERNAME") d %code.WriteLine(" s cChangeTime=$zdt($h,3,1)") d %code.WriteLine(" s cFieldList="""_%parameter("FieldList")_"""") d %code.WriteLine(" s cFieldList=$lfs(cFieldList,"","")") d %code.WriteLine(" s CurID=aID") s FldLst=%parameter("FieldList") s key="" for { s key=%compiledclass.Properties.Next(key) q:key="" s field=%compiledclass.Properties.GetAt(key).Name continue:'$lf($lfs($zcvt(FldLst,"U"),","),$zcvt(field,"U")) d %code.WriteLine(" &sql(SELECT "_field_" INTO :cValue FROM "_%class.Name_" WHERE ID=:CurID)") d %code.WriteLine(" s:SQLCODE cValue=""""") d %code.WriteLine(" s:cValue=$c(0) cValue=""""") d %code.WriteLine(" if aOperType=1") d %code.WriteLine(" {") d %code.WriteLine(" &sql(INSERT INTO Stm.ChangeLog (NameClass,NameField,UserName,ChangeTime,OperType,NValue,ObjID)") d %code.WriteLine(" VALUES (:cNameClass,'"_field_"',:cUserName,:cChangeTime,:aOperType,:cValue,:CurID))") d %code.WriteLine(" }") d %code.WriteLine(" if aOperType=-1") d %code.WriteLine(" {") d %code.WriteLine(" &sql(INSERT INTO Stm.ChangeLog (NameClass,NameField,UserName,ChangeTime,OperType,OValue,ObjID)") d %code.WriteLine(" VALUES (:cNameClass,'"_field_"',:cUserName,:cChangeTime,:aOperType,:cValue,:CurID))") d %code.WriteLine(" }") d %code.WriteLine(" if aOperType=0") d %code.WriteLine(" {") d %code.WriteLine(" s cOldValue=$g(^mtempChangeMonitor(cNameClass,CurID,"""_field_"""),"""")") d %code.WriteLine(" if cOldValue'=cValue") d %code.WriteLine(" {") d %code.WriteLine(" &sql(INSERT INTO Stm.ChangeLog (NameClass,NameField,UserName,ChangeTime,OperType,OValue,NValue,ObjID)") d %code.WriteLine(" VALUES (:cNameClass,'"_field_"',:cUserName,:cChangeTime,:aOperType,:cOldValue,:cValue,:CurID))") d %code.WriteLine(" }") d %code.WriteLine(" }") d %code.WriteLine(" k:$d(^mtempChangeMonitor(cNameClass,CurID,"""_field_""")) ^mtempChangeMonitor(cNameClass,CurID,"""_field_""")") } d %code.WriteLine(" q $$$OK ") q 1 } Все это в абстрактном классе. Пишется все в класс Stm.ChangeLog - обычный персистент, к тому же без методов. В классе, который логируем: 1) указываем в родителях этот абстрактный класс 2) Parameter FieldList = "Field1,Field2"; 3) Вызывается не в тригерах, а в %OnDelete и т.д. ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 07:22 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Langobard, Дык суть то в том, что нужно, чтобы записывались изменения в триггерах, то есть после выполнения SQL-запроса (Insert, Update), а не после сохранения объекта через %Save(). ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 07:54 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
VixlerLangobard, Дык суть то в том, что нужно, чтобы записывались изменения в триггерах, то есть после выполнения SQL-запроса (Insert, Update), а не после сохранения объекта через %Save(). Через триггер тоже можно :-) Только там при апдейте надо будет сохранять старые значения. У меня что-то типа такого: Trigger TUpdBef [ Event = UPDATE ] { s cID={ID} d ..SaveOld(0,cID) q } Trigger TUpdAft [ Event = UPDATE, Time = AFTER ] { s cID={ID} d ..ChangeLog(0,cID) q } Метод SaveOld - тоже в режиме objectgenerator. Там пишет "старые" значения в глобал ^mtempChangeMonitor (в предыдущем моем сообщение он есть) ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 08:30 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Эм... Тогда такой вопрос: зачем делали через CodeMode = objectgenerator? Какой в нём смысл в данной ситуации, почему нельзя написать без "d %code.WriteLine()"? Просто совсем мало с этим работал. ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 10:25 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Vixler, objectgenerator - чтобы перебирать названия полей через %compiledclass.Properties.GetAt(key).Name Не буду же я для каждого класса писать отдельный механизм логирования :-) Если нужно, чтобы класс логировался, то выполняем всего 2 действия: 1. Указываем ChangeMonitor в родителях; 2. Перечисляем, какие поля нужно логировать в параметре "FieldList". ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 10:49 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Langobard, Эм... А так? set CI = ##class(%Dictionary.CompiledClass).%OpenId(..%ClassName(1)) for i=1:1:CI.Properties.Count() { set prop = CI.Properties.GetAt(i) w prop.Name // Ищем в FieldList prop.Name } либо циклом по FieldList и set prop = CI.Properties.Find(FieldName) ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 12:07 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Vixler, Тоже вариант :-) Только с точки зрения быстродействия, Ваш вариант будет похуже. С objectgenerator'ом будут пройдены все поля в процессе компиляции, а в Вашем случае обход полей будет осуществляться в процессе выполнения. ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 12:59 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
Вот здесь вы вроде тоже проходитесь по всем полям: for { s key=%compiledclass.Properties.Next(key) q:key="" s field=%compiledclass.Properties.GetAt(key).Name continue:'$lf($lfs($zcvt(FldLst,"U"),","),$zcvt(field,"U")) } Эта часть по сути одинаковая, но вот дальше зачем d %code.WriteLine, не совсем понимаю, как оно даёт прирост скорости. ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 14:33 |
|
Триггеры и произвольные поля
|
|||
---|---|---|---|
#18+
VixlerВот здесь вы вроде тоже проходитесь по всем полям: for { s key=%compiledclass.Properties.Next(key) q:key="" s field=%compiledclass.Properties.GetAt(key).Name continue:'$lf($lfs($zcvt(FldLst,"U"),","),$zcvt(field,"U")) } Эта часть по сути одинаковая, но вот дальше зачем d %code.WriteLine, не совсем понимаю, как оно даёт прирост скорости. разница в том когда происходит этот обход objectgenerator выполняется во время компиляции класса, и после выполнения код этого метода будет уже другим и обходов там уже не будет. ... |
|||
:
Нравится:
Не нравится:
|
|||
27.08.2013, 14:36 |
|
|
start [/forum/search_topic.php?author=eddi&author_mode=last_posts&do_search=1]: |
0ms |
get settings: |
9ms |
get forum list: |
16ms |
get settings: |
12ms |
get forum list: |
16ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
34ms |
get topic data: |
13ms |
get forum data: |
3ms |
get page messages: |
53ms |
get tp. blocked users: |
1ms |
others: | 452ms |
total: | 617ms |
0 / 0 |