powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / Caché, Ensemble, DeepSee, MiniM, IRIS, GT.M [игнор отключен] [закрыт для гостей] / Триггеры и произвольные поля
14 сообщений из 14, страница 1 из 1
Триггеры и произвольные поля
    #38378031
Vixler
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Пытаюсь сделать запись изменений в объектах в триггерах. Для этого написал один базовый класс, от которого наследуются другие %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-запросом?
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378042
Блок А.Н.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Методы-генераторы вам нужно использовать.
Но это не слишком просто, даже лень писать :-)
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378051
Vixler
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да вот тоже думаю про генераторы, но вот как в такую функцию передать из триггера изменённые поля, не пойму... Айдишник можно передать, класснейм тоже, но сами поля, тем более неизвестно заранее, какие они будут... В самой функции нельзя выполнить что-то вроде Do %code.WriteLine(" write {ID}"), будет ругаться на {ID}. Короче, непонятно пока что.
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378052
D_De1mos
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Vixler,

Зная название свойства (в вашем случае через prop.Name) можно сделать такую конструкцию:
w $property(newHistory, prop.Name)
через $property так же можно присваивать данные
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378059
Vixler
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
D_De1mos,

Не, newHistory - это просто объект для записи изменений, он не является базовым классом для других, которые нужно журналировать.

То есть $property(newHistory, prop.Name) возвращает значение поля prop.Name у объекта newHistory, а нужно $property(##this, prop.Name), только это не будет работать, потому что Trigger по сути является ClassMethod'ом и ##this не будет иметь значения.

Но в то же время в Trigger передаются значения изменённых полей через {ID},{Prop1},{Prop2}, только Prop1 и Prop2 - это имена полей в разбираемом классе, а так как триггер наследуемый, то неизвестно, какими эти поля будут. Нужен какой-то общий механизм, наверное должен быть какой-то список значений с именами полей, передаваемый в триггер, но найти его не могу...
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378061
Langobard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
/// Добавление в лог
/// 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 и т.д.
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378073
Vixler
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Langobard,

Дык суть то в том, что нужно, чтобы записывались изменения в триггерах, то есть после выполнения SQL-запроса (Insert, Update), а не после сохранения объекта через %Save().
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378085
Langobard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
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 (в предыдущем моем сообщение он есть)
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378198
Vixler
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Эм... Тогда такой вопрос: зачем делали через CodeMode = objectgenerator? Какой в нём смысл в данной ситуации, почему нельзя написать без "d %code.WriteLine()"? Просто совсем мало с этим работал.
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378248
Langobard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Vixler,

objectgenerator - чтобы перебирать названия полей через %compiledclass.Properties.GetAt(key).Name
Не буду же я для каждого класса писать отдельный механизм логирования :-)

Если нужно, чтобы класс логировался, то выполняем всего 2 действия:
1. Указываем ChangeMonitor в родителях;
2. Перечисляем, какие поля нужно логировать в параметре "FieldList".
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378399
Vixler
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
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)
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378517
Langobard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Vixler,

Тоже вариант :-)

Только с точки зрения быстродействия, Ваш вариант будет похуже. С objectgenerator'ом будут пройдены все поля в процессе компиляции, а в Вашем случае обход полей будет осуществляться в процессе выполнения.
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378692
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, не совсем понимаю, как оно даёт прирост скорости.
...
Рейтинг: 0 / 0
Триггеры и произвольные поля
    #38378694
Фотография DAiMor
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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 выполняется во время компиляции класса, и после выполнения код этого метода будет уже другим и обходов там уже не будет.
...
Рейтинг: 0 / 0
14 сообщений из 14, страница 1 из 1
Форумы / Caché, Ensemble, DeepSee, MiniM, IRIS, GT.M [игнор отключен] [закрыт для гостей] / Триггеры и произвольные поля
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]