3

我正在尝试审核用户执行的导致相应表更改的操作。例如,如果用户要在 2 个账户之间转账,这将产生以下事件序列:

  1. 将转账金额插入转账表
  2. 从账户 1 的余额表中的余额中减去转账金额。
  3. 将转账金额添加到账户 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

因此,上述事件的顺序应该是:

  1. 更新传输表
  2. 插入 AuditRecord(父)
  3. 插入 AuditRecord(子)
  4. 插入 TransferAuditRecord
  5. 插入平衡表
  6. 插入 AuditRecord(子)
  7. 插入 BalanceAuditRecord
  8. 插入平衡表
  9. 插入 AuditRecord(子)
  10. 插入 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。

4

2 回答 2

1

似乎 parentAuditRecord 与 transferauditrecord 和 balance auditrecord 之间的关系不应该是一对多的。当我阅读您输入的内容时,我将其视为该审计层次结构的每个子类使用的表,这是一对一的关系。

http://www.hibernate.org/hib_docs/reference/en/html/inheritance.html

您可能还想查看 JBoss 的 Envers 项目。

于 2008-11-11T20:40:07.800 回答
0

在设计级别,似乎只有插入的 db 设计在这里会产生奇迹。

如果您想保持现在的状态(我相信您会这样做),您可以查看 Hibernate 侦听器/拦截器/事件(在文档中明确定义: http ://www.hibernate.org/hib_docs /v3/reference/en-US/html_single/ )

否则,我刚刚查看了 JBoss Envers,它似乎也非常有用。

于 2008-11-20T01:21:15.880 回答