События в реляционных СУБД

добавлено: 07 дек 15
понравилось:0
просмотров: 2585
комментов: 7

теги:

Автор: Old Nick

Как сделать события в базе данных

Для этого нужны события и обработчики событий
Создадим класс Событие (Event), наследник от Object

1. Добавим записи в таблицы ClassTree и ClassTreePath (см. предыдущую статью)
2. Создадим таблицу для класса Event

create table TEvent
(
  OID int primary key clustered
)
go


3. Создадим представление

create view VEvent  
as
  select o.*
    from TEvent e
           join
         VObject o
           on o.OID = e.OID
go


4. Создадим триггер

create trigger Event_Insert
on VEvent
for insert
as
  declare @OID int
  declare Cur cursor fast_forward for
    select Name, Caption
      from inserted
  open Cur
    fetch next from Cur into @Name, @Caption
    while @@fetch_status = 0
    begin
      insert VObject ( Name, Caption )
        select @Name, @Caption
     
      set @OID = @@IDENTITY
      insert TEvent ( OID )
        select @OID
      update TObject set Class = 'Event' where OID = @OID
      fetch next from Cur into @Name, @Caption
    end
  close Cur
  deallocate Cur  
go


5. Зарегистрируем событие 'Create'

insert VEvent ( Name, Caption )
  values ( 'Create', 'Создание нового объекта' )


6. Создадим таблицу для обработчиков событий

create table TEvent_Handlers
(
  OID     int,           -- ссылка на событие
  Handler varchar(256),  -- поле для хранения имени хранимой процедуры - обработчика событий
  primary key clustered ( OID, Handler )
)
go


7. Зарегистрируем обработчик события
-- для начала напишем хранимку, которая проверяет уникальность номеров и дат документов

create procedure Document_OnCreate
  @OID int
as
  declare @Number varchar(256),
          @Date   datetime
  select @Number = Number,
         @Date   = Date
    from VDocument
    where OID = @OID
  if exists(select * from VDocument where Date = @Date and Number = @Number and OID <> @OID)
    raiserror('Документ с таким номером и датой уже есть в системе', 11, 1)
go


-- Теперь зарегистрируем обработчик

declare @EventOID int
select @EventOID = OID from VEvent where Name = 'Create'
insert TEvent_Handlers ( OID, Handler )
  select @EventOID, 'Document_OnCreate'


8. Создадим метод возбуждения события

create procedure Event_OnCreate_Throw
  @SenderOID int -- идентификатор объекта, который бросает событие
as
  declare @EventOID int
  select @EventOID = OID from VEvent where Name = 'Create'
  declare Cur cursor fast_forward for
    select h.ProcName
      from TEvent_Handlers h
      where h.OID = @EventOID
  open Cur
    fetch next from Cur into @ProcName
    while @@fetch_status = 0
    begin
      exec @ProcName @SenderOID
      fetch next from Cur into @ProcName
    end
  close Cur
  deallocate Cur
go


Данная хранимая процедура перебирает все обработчики из зарегистрированных для этого события
и вызывает их по очереди.

9. Теперь надо вставить возбуждение события в нужное место. Для этого изменим trigger

alter trigger Document_Insert
on VDocument
for insert
as
  declare @OID     int,
          @Class   varchar(256
          @Name    varchar(256),
          @Caption varchar(256),
          @Date    datetime,
          @Number  varchar(256
  declare Cur cursor fast_forward for
    select Class, Name, Caption, Date, Number
      from inserted
  open Cur
    fetch next from Cur into @Class, @Name, @Caption, @Date, @Number
    while @@fetch_status = 0
    begin
      if @Class is null
        set @Class = 'Document'
      insert VObject ( Class, Name, Caption )
        select @Class, @Name, @Caption
     
      set @OID = @@IDENTITY
      insert TDocument ( OID, Date, Number )
        select @OID, @Date, @Number
      
      if @Class = 'Document'
        exec Event_OnCreate_Throw @OID

      fetch next from Cur into @Class, @Name, @Caption, @Date, @Number
    end
  close Cur
  deallocate Cur  
go


Возбуждение события нужно вставлять в триггеры for insert для всех классов,
но вызываться они будут только в конечном триггере, когда имя класса будет соответствовать триггеру
Это нужно для того, чтобы при обработке события во всех таблицах класса уже были данные вновь созданного объекта.

Это простой вид синхронного события и фантазии по реализации здесь нет предела. Можно построить полность
событийную систему. Главное не увлечься процессом, а оптимально решить поставленную прикладную задачу.

В следующей статье опишу асинхронные события.

Комментарии


  • А зачем это надо, если действительно триггеры есть?
    И потом в каком порядке будут вызываться события в иерархии классов, например, события "BeforeUpdate", "Afterupdate"

  • Для такого механизма можно сделать интерфейс настройки. Включать/выключать событие и обработчики, менять очередность обработчиков, смотреть лог обработки (логирование я не описал)

  • Ты ведь сам пишешь когда срабатывать событию. Как сам напишешь, тогда и будет срабатывать

  • В ЛИНТЕР есть механизмы, которые позволяют описываемое сделать практически штатно, но классы сами и их иерархию всё равно нужно делать. Важно, что не нужна таблица, на которую могут быть атаки тем или иными способом...

  • Ссылку в студию. Что такое ЛИНТЕР?

  • https://ru.wikipedia.org/wiki/%D0%9B%D0%98%D0%9D%D0%A2%D0%95%D0%A0
    http://linter.ru
    версия, например, под LINUX x86_64 http://goo.gl/Mzt0Vd

  • В SQL server тоже еть достаточно мощный механизм. Называется Service Broker. Как раз вся асинхронная обработка его использует. Например отправка почты на нем реализована.



Необходимо войти на сайт, чтобы оставлять комментарии