Спасибо всем за ваши комментарии.
Позвольте мне показать решение, которое я придумала. Это просто идея в процессе разработки, но я надеюсь, что она приведет к чему-то: мне понадобится таблица «audit_lock», которая всегда содержит запись для текущей транзакции, содержащую информацию о текущей транзакции, особенно имя пользователя и уникальный идентификатор_транзакции (UUID) или похожие). Эта строка будет вставлена при запуске транзакции и удалена перед ее фиксацией.
Затем у меня есть таблица generi c audit_trail, содержащая проверенную информацию.
Все проверенные таблицы заполняют шаблон c таблица контрольного журнала с использованием триггеров, сериализующая каждый проверенный столбец в отдельную запись общей таблицы c контрольного журнала.
Для таблицы Audit_lock и audit_trail необходимо использовать блокировку строк. Также, чтобы избежать блокировок чтения в таблице aud_lock, нам нужно установить уровень изоляции на COMMITTED READ LAST COMMITTED. Если ваш вариант использования не поддерживает это, предложенный шаблон не работает.
Вот DDL:
CREATE TABLE audit_lock
(
transaction_id varchar(40) primary key,
username varchar(40)
);
alter table audit_lock
lock mode(ROW);
CREATE TABLE audit_trail
(
id serial primary key,
tablename varchar(255) NOT NULL,
record_id numeric(10) NOT NULL,
username varchar(40) NOT NULL,
transaction_id varchar(40) NOT NULL,
changed_column_name varchar(40),
old_value varchar(40),
new_value varchar(40),
operation varchar(40) NOT NULL,
operation_date datetime year to second NOT NULL
);
alter table audit_trail
lock mode(ROW);
Теперь нам нужно иметь проверенную таблицу:
CREATE TABLE audited_table
(
id serial,
somecolumn varchar(40)
);
И в таблице есть триггер вставки, записывающий в Audit_trail:
CREATE PROCEDURE proc_trigger_audit_audited_table ()
REFERENCING OLD AS o NEW AS n FOR audited_table;
INSERT INTO audit_trail
(
tablename,
record_id,
username,
transaction_id,
changed_column_name,
old_value,
new_value,
operation,
operation_date
)
VALUES
(
'audited_table',
n.id,
(SELECT username FROM audit_lock),
(SELECT transaction_id FROM audit_lock),
'somecolumn',
'',
n.somecolumn,
'INSERT',
sysdate
);
END PROCEDURE;
CREATE TRIGGER audit_insert_audited_table INSERT ON audited_table REFERENCING NEW AS post
FOR EACH ROW(EXECUTE PROCEDURE proc_trigger_audit_audited_table() WITH TRIGGER REFERENCES);
Теперь давайте воспользуемся этим: Сначала вызывающей стороне транзакции необходимо сгенерировать для себя транзакцию_id, возможно, используя механизм генерации UUID. В приведенном ниже примере значениеaction_id просто равно «4711».
BEGIN WORK;
SET ISOLATION TO COMMITTED READ LAST COMMITTED; --should be set globally
-- Issue the generation of the audit_lock entry at the beginnig of each transaction
insert into audit_lock (transaction_id, username) values ('4711', 'userA');
-- Is it there?
select * from audit_lock;
-- do data manipulation stuff
insert into audited_table (somecolumn) values ('valueA');
-- Issue that at the end of each transaction
delete from audit_lock
where transaction_id = '4711';
commit;
В быстром тесте все это работало даже в одновременных транзакциях. Конечно, это все еще требует большой работы и тестирования, но в настоящее время я надеюсь, что путь возможен.
Позвольте мне также добавить немного больше информации о другом подходе, который мы используем в Oracle: В Oracle мы (ab) используем имя транзакции, чтобы хранить именно ту информацию, которая в приведенном выше предложении хранится в таблице audit_lock.
Остальное - то же, что и выше. Триггеры отлично работают в этом конкретном приложении c, хотя, конечно, существует множество сценариев ios для других приложений, где установка триггеров вставки, удаления и обновления для каждой таблицы создает записи для каждого измененного столбца в таблице. быть сумасшедшим В нашем приложении оно отлично работает уже десять лет и не оказывает заметного влияния на производительность при использовании приложения.
На сервере приложений java все блоки кода, которые изменяют данные, начинаются с настройки сначала имя транзакции, а затем множество изменений в различных таблицах, которые могут вызывать все эти триггеры. Все они выполняются в одной и той же транзакции, и, поскольку она имеет имя транзакции, которое содержит пользователя приложения, триггеры могут записать эту информацию в таблицу контрольного журнала.
Я знаю, что существуют другие подходы к проблеме, и вы могли бы даже сделать это только с функциями гибернации, но наш подход позволяет нам обеспечить некоторую согласованность через базу данных (ограничение NOT NULL в таблице журнала аудита для имени пользователя). Поскольку все выполняется с помощью триггеров, мы можем позволить этим сбоям, если имя транзакции не содержит пользователя (требуя, чтобы оно было в указанном формате c). Если есть какие-либо другие части приложения, другие приложения или невежественные администраторы, пытающиеся выпускать обновления для проверенных таблиц без учета установки имени транзакции в указанном формате c, эти обновления завершатся неудачно. Это делает обновления проверяемых таблиц, которые не генерируют требуемые записи в таблице аудита, более трудными (конечно, не невозможными, конечно, недоброжелательный администратор может сделать что-либо). позвольте мне процитировать Луиса: может показаться ужасной идеей, но у меня есть свой вариант использования;)
Идея @ Luís создавать в каждой транзакции конкретную c таблицу для хранения информации вызывает проблему блокировки в systables. Давайте назовем это «таблица информации о транзакции». Эта идея не пришла мне в голову, поскольку DDL вызывает коммиты в Oracle. Поэтому я попытался сделать это в Informix, но если я попытаюсь создать таблицу с именем «tblX» в двух одновременных транзакциях, вторая транзакция получит исключение блокировки:
Cannot update system catalog (systables). [SQL State=IX000, DB Errorcode=-312]
Next: ISAM error: key value locked [SQL State=IX000, DB Errorcode=-144]
Но разрешив всем транзакциям использовать ту же таблицу, что и выше работает, насколько я тестировал это прямо сейчас.