【问题标题】:Audit Schema Mapping with Hibernate and AOP使用 Hibernate 和 AOP 审计模式映射
【发布时间】:2010-09-21 20:58:45
【问题描述】:

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

  1. 将转账金额插入转账表
  2. 从账户 1 的余额表中的余额中减去转账金额。
  3. 将转账金额添加到账户 2 余额表中的余额。

所有表的父审计消息将是:“用户生成的金额为 XXX 的转账”

这是通过以下架构实现的: schema

alt text 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 年 11 月 11 日 12:00 通过"

编辑我决定显式映射多对多表(带有关联的接口),然后在 afterTransactionCompletion 中调用父审计记录上的保存(将保存级联到子审计记录)然后在所有子映射表上显式保存接口。这不是真正的审计历史,而是记录用户操作的非侵入性方法。如果我以后需要更完整的审计历史记录,我会研究 Envers。

【问题讨论】:

    标签: java hibernate audit


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      在设计级别,似乎只有插入的 db 设计在这里可以发挥惊人的作用。

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

      否则,我刚刚研究了 JBoss Envers,它似乎也很有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-28
        • 2012-10-27
        • 1970-01-01
        • 2018-04-18
        • 2019-05-25
        相关资源
        最近更新 更多