1. 创建日志表与触发器函数
-- 1. 创建通用日志表
CREATE TABLE t_case_log (
log_id BIGSERIAL PRIMARY KEY,
case_id BIGINT,
operation VARCHAR(10), -- INSERT, UPDATE, DELETE
old_data JSONB, -- 修改/删除前的数据
new_data JSONB, -- 新增/修改后的数据
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
changed_by VARCHAR(50) DEFAULT CURRENT_USER
);
-- 2. 创建触发器函数
CREATE OR REPLACE FUNCTION process_t_case_audit()
RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO t_case_log(case_id, operation, old_data)
VALUES (OLD.id, 'DELETE', to_jsonb(OLD));
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO t_case_log(case_id, operation, old_data, new_data)
VALUES (NEW.id, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW));
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO t_case_log(case_id, operation, new_data)
VALUES (NEW.id, 'INSERT', to_jsonb(NEW));
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 3. 将触发器绑定到 t_case 表
CREATE TRIGGER t_case_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON t_case
FOR EACH ROW EXECUTE FUNCTION process_t_case_audit();
2. 查询审计日志
直接在 PostgreSQL 执行 SQL 或通过 Spring Data JPA 映射 t_case_log 进行查询:
SELECT * FROM t_case_log WHERE case_id = 1001 ORDER BY changed_at DESC;
这段 SQL 脚本实现了一个数据库层面的自动数据审计机制 。当对 t_case 表执行新增、修改或删除(CUD)时,PostgreSQL 会自动捕获数据变动,并将变更前后的完整快照以 JSON 格式写入 t_case_log 日志表。
一、 审计日志表设计 (t_case_log)
-
log_id:日志主键,使用BIGSERIAL实现自增。 -
case_id:关联的目标记录 ID。 -
operation:记录操作类型,仅包含INSERT、UPDATE、DELETE三种。 -
old_data/new_data:核心设计,采用 PostgreSQL 的JSONB类型。直接将整行数据转为 JSON 存储,优势在于即便将来t_case表增删字段,该日志表结构也无需任何修改。 -
changed_at/changed_by:利用数据库默认值(CURRENT_TIMESTAMP和CURRENT_USER)自动记录变动发生的时间与数据库操作账号。
二、 触发器函数逻辑 (process_t_case_audit)
函数基于 PL/pgSQL 编写,利用了 PostgreSQL 的内置触发器变量:
-
内置变量解析:
-
TG_OP:存储触发当前函数的 SQL 类型(INSERT/UPDATE/DELETE)。 -
OLD:代表变更或删除前 的整行记录(仅在UPDATE和DELETE时可用)。 -
NEW:代表新增或修改后 的整行记录(仅在INSERT和UPDATE时可用)。 -
to_jsonb(...):PG 内置函数,直接将一行 Row 记录转换为 JSONB 对象。
-
-
分流逻辑:
-
DELETE :仅提取并记录
OLD数据(删除前的快照)。 -
UPDATE :同时记录
OLD(修改前)和NEW(修改后),方便后期做属性级别的差异对比(Diff)。 -
INSERT :仅提取并记录
NEW数据(新插入的数据)。
-
三、 绑定触发器 (t_case_audit_trigger)
-
AFTER INSERT OR UPDATE OR DELETE:采用 事后触发(AFTER) 。只有在t_case的数据修改成功后才会写入日志;若主语句因约束等原因报错回滚,日志写入也会同步回滚,确保审计数据的绝对真实性。 -
FOR EACH ROW:行级触发器。如果是批量修改(例如一条 SQL 更新了 10 条记录),函数会被调用 10 次,生成 10 条精准的日志。
💡 实际落地时的注意事项
-
应用层操作人问题 :
changed_by默认记录的是 PG 的数据库连接用户(如postgres)。如果是 Spring Boot 应用通过连接池操作,这里记录的永远是连接池账号,无法体现"系统登录的具体用户" 。如果需要记录业务用户 ID,通常需要配合 PG 的临时变量(SET LOCAL app.current_user_id = 'xxx')或在应用层(如 Hibernate Envers / AOP)处理。 -
写性能开销 : 由于是
AFTER ... FOR EACH ROW触发器,每一次对t_case的写入都会同步带来一次对t_case_log的写入(1:1 写放大)。在大批量数据导入或高并发写入场景下,需注意评估 IO 负载。