Files
his/audit_field_solution.md

4.0 KiB
Raw Blame History

关于数据库审计字段create_by, create_time等的处理方案

问题描述

在使用OpenHIS系统时可能会遇到如下错误

org.postgresql.util.PSQLException: ERROR: null value in column "create_by" of relation "adm_practitioner" violates not-null constraint

问题分析

  1. 数据库表中的审计字段如create_by, create_time设置了NOT NULL约束
  2. 应用程序层面使用了MyBatis-Plus的自动填充功能来设置这些字段
  3. 当自动填充机制失效时,就会出现违反非空约束的错误

解决方案

方案一:修复自动填充机制(推荐)

系统已经实现了自动填充机制,位于 MybastisColumnsHandler.java

// 设置数据新增时候的,字段自动赋值规则
@Override
public void insertFill(MetaObject metaObject) {
    // 同时填充驼峰和下划线命名的字段,以兼容不同的配置
    this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
    this.strictInsertFill(metaObject, "create_time", Date.class, new Date());

    String username = "system";
    try {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser != null) {
            username = loginUser.getUsername();
        }
    } catch (Exception ignored) {
    }
    // 使用 fillStrategy 确保即使字段为 null 也会被填充
    this.strictInsertFill(metaObject, "createBy", String.class, username);
    this.strictInsertFill(metaObject, "create_by", String.class, username);
    // 如果 strictInsertFill 没有生效,使用 setFieldValByName 强制设置
    if (metaObject.hasGetter("createBy") && metaObject.getValue("createBy") == null) {
        this.setFieldValByName("createBy", username, metaObject);
    }
    if (metaObject.hasGetter("create_by") && metaObject.getValue("create_by") == null) {
        this.setFieldValByName("create_by", username, metaObject);
    }
    ...
}

确保所有实体类都继承自 HisBaseEntityBaseEntity,这样就能自动获得审计字段。

方案二:移除数据库约束(谨慎使用)

如果确实需要允许审计字段为NULL可以移除数据库约束

-- 移除 adm_practitioner 表中 create_by 列的 NOT NULL 约束
ALTER TABLE "public"."adm_practitioner" 
ALTER COLUMN "create_by" DROP NOT NULL;

-- 同样处理 create_time 列(如果需要)
ALTER TABLE "public"."adm_practitioner" 
ALTER COLUMN "create_time" DROP NOT NULL;

方案三:批量修复所有表的约束

如果多个表都存在这个问题,可以使用以下脚本:

-- 为所有表的审计字段移除NOT NULL约束
-- 注意:执行前请备份数据库!

-- 1. 检查所有包含审计字段的表
SELECT 
    table_name,
    column_name,
    is_nullable
FROM 
    information_schema.columns 
WHERE 
    column_name IN ('create_by', 'create_time', 'update_by', 'update_time')
    AND table_schema = 'public'
    AND is_nullable = 'NO';  -- NO 表示 NOT NULL 约束

-- 2. 根据需要移除特定表的约束
-- 示例移除多个表的create_by约束
ALTER TABLE "public"."adm_practitioner" ALTER COLUMN "create_by" DROP NOT NULL;
ALTER TABLE "public"."adm_patient" ALTER COLUMN "create_by" DROP NOT NULL;
-- 添加更多表的处理...

最佳实践

1. 确保实体类继承基础类

所有实体类应继承 HisBaseEntityBaseEntity

@Data
@TableName("adm_practitioner")
public class Practitioner extends HisBaseEntity {
    // 其他字段...
}

2. 检查安全上下文

确保在保存数据时有有效的安全上下文,这样自动填充处理器才能获取到当前用户信息。

3. 验证自动填充配置

确保 MybastisColumnsHandler 在Spring容器中被正确注册使用@Component注解

总结

  • 推荐保持数据库中的NOT NULL约束确保数据完整性
  • 依赖MyBatis-Plus的自动填充机制来设置审计字段
  • 确保所有实体类继承基础实体类
  • 在必要时才考虑移除数据库约束