用Spring来开发一个WEb系统。比如:现在实体 Problem,Problem有一下属性:Cateory,Title,description。需求是可以更新Problem的数据库记录,但是,用户可以在页面上看到这个Problem的历史记录,需要看到具体被修改属性的原始值跟新的值。没有好的设计思路。求解。。。
看到这个需求,我第一反应是设计在java层做更新记录好像更方便。不需要任何其他知识点了。
数据库加[b]一张[/b]表就行,比如名叫record
表里面 entry property old new date 类似这么几个字段,java层再加一个这个表对应的实体类Record,并且有一个insert方法
在Problem的update方法里,很容易能获取到这几个值,update成功以后创建一个Record对象insert到表里。 这样做的好处是可以收集任意一个实体类的修改记录,可能有User,Person等等。查询的时候根据entry name这个字段来区分。这样无论多少次修改,都能保存直观的历史记录。
至于这样污染了update的代码,你可以思考下依靠spring的aop编程,比如后置通知之类的,将比较新老值,组装成Record对象等逻辑,分离到一个通用、独立的方法里。其实这一点还是需要思考一下,如果才能更优雅地实现。
可以考虑分表,一个历史表一个原始表,每次更新的时候将原始数据存到历史表中,然后在更新原始表
1、版本号
每修改一下 复制一条记录 并版本号+1
2、历史表
可以通过如下完成复制:
1、触发器
2、应用程序内
可以创建一个与原表结构相同的历史表,可以比原表多一些数据操作的标记字段,然后对原表添加Trigger,在原表进行Update,Insert,Delete操作的时候向历史表添加数据(可以考虑同时添加Before Image和After Image).
Trigger可以做得更简单一些。假设原表的表名是TBL_AAA,有若干列A1,A2...,那么Audit表就可以为AUD_AAA,设有A1,A2...,AUDIT_DT,AUDIT_MODE,AUD_IMG_TYPE;
[code="java"]
CREATE OR REPLACE TRIGGER TRI_AAA
AFTER INSERT OR UPDATE
ON TBL_AAA
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
auditMode VARCHAR2 (6);
curr_date date;
BEGIN
IF UPDATING THEN
auditMode := 'update';
ELSIF INSERTING THEN
auditMode := 'insert';
END IF;
curr_date:=sysdate;
IF UPDATING THEN
insert into AUD_AAA
(
A1,
A2,
.,
.,
.,
AUDIT_MODE,
AUD_IMG_TYPE,
AUDIT_DT
) values
(
:OLD.A1,
:OLD.A2,
:OLD..,
:OLD..,
:OLD..,
auditMode,
'before',
curr_date
);
END IF;
IF INSERTING or UPDATING THEN
insert into AUD_AAA
(
A1,
A2,
.,
.,
.,
AUDIT_MODE,
AUDIT_IMG_TYPE,
AUDIT_DT
) values
(
:NEW.A1,
:NEW.A2,
:NEW..,
:NEW..,
:NEW..,
.auditMode,
'after',
curr_date
);
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
[/code]
如果是新建一个历史表,每次复制,这样能查询出历史版本,但是如果要知道修改的字段,还需要 取出数据对比才知道,
如果新建的表是记录 old值与new值的这种表,那么就很方便查看修改前的值与修改后的值,但这种方式不能查看历史某一版本的全部数据,根据需求把握,我以前是用的第二种方案,记录 用户修改属性,然后让管理员审核,可以只通过部分字段或者驳回部分字段。
两种方式,
一种是简历一张和原表一样的表,对每个可能修改的字段列增加一个修改值的列,再增加一个字段是版本号,这样可以随意调取任何一个版本的记录,速度快,缺点是不可复用,只能针对某一个表,如果你的原表是主子表,那你新增的表就要和原表的结构一样,比较比如原表是合同表,有合同项子表,有订单子表,你都要一一复制。
第二种是使用修改表,建一张修改记录主表,记录修改时间,修改人,审核人等主信息,配一张修改项子表,记录修改的表名或者对象名,对象主键id,修改的对象的属性名,原值,修改值,如果是子对象,可以在对象名和对象id上拼接。这种方式适用比较多的情况,具有通用性,但是调取任何一个版本,你需要一个复原引擎,读取你指定版本之后每一个版本,进过处理才能还原到指定版本。
两种方式的选择需要自己判断一下需求,选择适合自己的方式,至于到底是触发器控制,还是对修改逻辑进行监控就没那么重要了。