我使用 postgresql 11
我有一个表employee_audit,我运行一个触发器,它工作成功。
这是代码:
CREATE TABLE audit_log.employee_audit
(
operation char(15) NOT NULL,
stamp timestamp NOT NULL,
userid text ,
id character varying(15) NOT NULL,
tel character varying(15),
fax character varying(15),
mail character varying(100)
created_by character varying(50),
created_date timestamp without time zone,
last_modified_by character varying(50),
last_modified_date timestamp without time zone
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE audit_log.employee_audit
OWNER to postgres;
------------------------------------- Fin création table employee_audit
------- Create the function to insert into the log table
CREATE OR REPLACE FUNCTION audit_log.process_employee_audit() RETURNS TRIGGER AS $employee_audit$
BEGIN
--
-- Create a row in emp_audit to reflect the operation performed on emp,
-- make use of the special variable TG_OP to work out the operation.
--
IF (TG_OP = 'DELETE') THEN
INSERT INTO audit_log.employee_audit SELECT 'DELETE', now(), NEW.last_modified_by, OLD.*;
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
IF (TG_WHEN='AFTER') THEN
INSERT INTO audit_log.employee_audit SELECT 'AFTER UPDATE', now(), NEW.last_modified_by, NEW.*;
ELSIF (TG_WHEN='BEFORE') THEN
INSERT INTO audit_log.employee_audit SELECT 'BEFORE UPDATE', now(), OLD.last_modified_by, OLD.*;
END IF;
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO audit_log.employee_audit SELECT 'INSERT', now(), NEW.created_by, NEW.*;
RETURN NEW;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$employee_audit$ LANGUAGE plpgsql;
-------- Create AFTER trigger on the employee table
CREATE TRIGGER employee_audit
AFTER INSERT OR UPDATE OR DELETE ON public.employee
FOR EACH ROW EXECUTE PROCEDURE audit_log.process_employee_audit();
-------- Crete Before Trigger on the employee Table
CREATE TRIGGER employee_audit_before
Before UPDATE ON public.employee
FOR EACH ROW EXECUTE PROCEDURE audit_log.process_employee_audit();
我在更新中有一个用例,特别是在“更新后”
列 userid 将由 NEW.last_modified_by
填充在我的更新功能应用程序中,我填写了此列last_modified_by 由连接的用户。
这是代码:
public UnitDTO updateUnit(UnitDTO unitDTO) {
User user = userService.getUserWithAuthorities().get();
Unit unit = unitMapper.toEntity(unitDTO);
unit.setLastModifiedBy(user.getEmployee().getId());
unit.setLastModifiedDate(LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()));
unit = unitRepository.save(unit);
return newUnitDTO;
}
并且在表employee_audit中,列userid包含正确的数据,即 应用程序的连接用户(进行更新的人)。
我现在的问题是,当我直接连接到数据库并更新 employee 表中的一些数据时
在employee_audit中我发现列userid包含last_modified_by的数据 已经插入的列。
正确的值应该是数据库的用户,因为我进行了更新 直接在数据库中 或者应该是空的
我认为我应该在“更新后”情况下的触发器中设置一些条件 (所以改变NEW.last_modified_by)
您记录谁在何时做了什么的方法是错误的。您不能依赖您的应用程序执行此操作,正是因为,就像您现在注意到的那样,您不能保证每个查询都会由它生成。
首先,您需要创建您的应用程序,以便每个应用程序用户都是数据库中的登录名。 Postgres 有一个
session_user
变量返回该登录名(只有超级用户可以更改其值)。如果您的应用程序使用通用登录名+存储在您创建的表中的用户记录进行连接,则需要更改它。
在纠正此问题的同时,您还可以使审核系统更加通用:
CREATE TABLE audit_template
(
created_by name NOT NULL,
created_date timestamp without time zone NOT NULL,
last_modified_by name NOT NULL,
last_modified_date timestamp without time zone NOT NULL
);
CREATE OR REPLACE FUNCTION audit_set_user()
RETURNS TRIGGER
LANGUAGE 'plpgsql'
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
IF TG_OP = 'INSERT' THEN
NEW.created_by := SESSION_USER;
NEW.created_date := current_timestamp;
END IF;
NEW.last_modified_by := SESSION_USER;
NEW.last_modified_date := current_timestamp;
RETURN NEW;
END;
$BODY$;
请注意,我使用
NOT NULL
声明了表,并且没有列的默认值,因为触发器将负责填充缺失的位。
employee
表和关联的 BEFORE
触发器:员工继承
audit_template
:
CREATE TABLE employee
(
stamp timestamp NOT NULL,
userid text ,
id character varying(15) NOT NULL,
tel character varying(15),
fax character varying(15),
mail character varying(100)
) INHERITS (audit_template);
CREATE TRIGGER employee_fill_audit
BEFORE INSERT OR UPDATE OR DELETE ON employee
FOR EACH ROW EXECUTE PROCEDURE audit_set_user();
在此阶段,触发器将确保审核数据正确填写到
employee
表中。id
、mail
等)。
CREATE TABLE employee_audit
(
stamp timestamp NOT NULL,
userid text ,
id character varying(15) NOT NULL,
tel character varying(15),
fax character varying(15),
mail character varying(100),
OP TEXT NOT NULL
) INHERITS (audit_template);
CREATE OR REPLACE FUNCTION employee_audit_log()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
If TG_OP = 'INSERT' THEN
INSERT INTO employee_audit SELECT New.*, TG_OP;
ELSE
INSERT INTO employee_audit SELECT Old.*, TG_OP;
END IF;
RETURN NEW;
END;
$BODY$;
CREATE TRIGGER employee_log_to_audit
AFTER INSERT OR UPDATE OR DELETE ON employee
FOR EACH ROW EXECUTE PROCEDURE employee_audit_log();
还有一种创建
employee_audit
的替代方法,可以通过这种方式完成(以及对触发器的一些更改):
CREATE TABLE employee_audit
(
OP TEXT NOT NULL
) INHERITS (employee);
不过我不太喜欢它,因为它会导致
SELECT * FROM employee
返回两个表的内容。要从 employee
获取没有 employee_audit
的内容,您必须使用查询:
SELECT * FROM ONLY employee
我让您在以下每个查询之后执行
SELECT * FROM employee
和 SELECT * FROM employee_audit
:
INSERT INTO employee(stamp, id) VALUES (now(), 'Some id');
UPDATE employee SET id = 'Some other id';
DELETE FROM employee;