我正在尝试审核用户执行的导致相应表更改的操作。例如,如果用户要在 2 个账户之间转账,这将产生以下事件序列:
- 将转账金额插入转账表
- 从账户 1 的余额表中的余额中减去转账金额。
- 将转账金额添加到账户 2 的余额表中的余额。
所有表的父审计消息将是:“用户生成的金额为 XXX 的转账”
这是通过以下模式实现的: 模式
替代文字 http://img48.imageshack.us/img48/7460/auditloggingiv6.png
问题是我如何在hibernate中表示这个?
我创建了以下内容:
在 Balance and Transfer 的映射文件中
<set name="auditRecords" table="TransferAuditRecord" inverse="false" cascade="save-update">
<key>
<column name="AuditRecordID" not-null="true" />
</key>
<one-to-many class="audit.AuditRecord"/>
</set>
Transfer 和 Balance 类然后实现具有方法的 IAuditable
public void setAuditRecords(Set<AuditRecord> auditRecord);
public Set<AuditRecord> getAuditRecords();
在 AuditRecord 的映射文件中,我有:
<many-to-one name="parentAuditRecord" lazy="false"
column="parent_id"
class="audit.AuditRecord"
cascade="all" />
然后在使用 AOP 和 Hibernate 拦截器的 Logging 类中,我有:
AuditRecord auditRecord = new AuditRecord();
auditRecord.setUser(userDAO.findById(
org.springframework.security.context.SecurityContextHolder.getContext()
.getAuthentication().getName()));
auditRecord.setParentAuditRecord(getCurrentActiveServiceRecord());
auditable.getAuditRecords().add(auditRecord);
然后在服务类中,我调用以下方法,包含在事务中:
save(balance1);
save(balance2);
transfer.setPassed(true);
update(transfer);
parentAuditRecord 是使用带有线程安全堆栈的 AOP 创建的,并且 AuditRecordType_id 是使用方法上的注释设置的。
我遗漏了转移表上的“通过”列。以前我调用 save(transfer) 将转账金额插入到 Transfer 表中,并将传递设置为 false。(此操作也经过审核)。
我的要求比上面的例子稍微复杂一些:P
因此,上述事件的顺序应该是:
- 更新传输表
- 插入 AuditRecord(父)
- 插入 AuditRecord(子)
- 插入 TransferAuditRecord
- 插入平衡表
- 插入 AuditRecord(子)
- 插入 BalanceAuditRecord
- 插入平衡表
- 插入 AuditRecord(子)
- 插入 BalanceAuditRecord
但是,上面定义的级联选项在更新语句中失败。Hibernate 拒绝将记录插入到多对多表中(即使 AuditRecord 映射上的 unsaved-value="any" )。我总是想将行插入到多对多表中,因此一个传输可能有许多标记先前事件的审计记录。但是,最新的事件决定了用户想要看到的消息。Hibernate 要么尝试更新多对多表和以前的 AuditRecord 条目,要么只是拒绝插入 AuditRecord 和 TransferAuditRecord,从而引发 TransientObjectException。
审核消息的检索方式如下:
msg=... + ((AuditRecord) balance.getAuditRecords().toArray()[getAuditRecords().size()-1])
.getParentAuditRecord().getAuditRecordType().getDescription() + ...;
消息应该是这样的:“用户名设置转移到 2008 年 10 月 11 日 12:00 传递”
编辑我决定显式映射多对多表(具有关联的接口),然后在 afterTransactionCompletion 中,调用父审计记录上的保存(将保存级联到子审计记录),然后显式保存接口在所有子映射表上。这不是真正的审计历史,而是记录用户操作的非侵入性方法。如果我以后需要更完整的审计历史,我会研究 Envers。