使用Hibernate和AOP进行审计模式映射

时间:2008-11-11 20:20:35

标签: java hibernate audit

我正在尝试审核用户执行的操作导致对相应表的更改。例如,如果用户要在两个帐户之间转移资金,则会生成以下事件序列:

  1. 将转帐金额插入转帐表
  2. 从帐户1的余额表中的余额中减去转帐金额。
  3. 在帐户2的余额表中为余额添加转帐金额。
  4. 所有表的父审核消息将是:“用户生成的金额转移金额”

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

    alt text http://img48.imageshack.us/img48/7460/auditloggingiv6.png

    问题是如何在休眠中表示这一点?

    我创建了以下内容:

    在Balance和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>
    

    转移和平衡类然后实现具有方法的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 Interceptor的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);
    

    使用带有线程安全堆栈的AOP创建parentAuditRecord,并使用方法上的注释设置AuditRecordType_id。

    我遗漏了转移表上的“通过”列。以前我调用save(transfer)将传输量插入Transfer表,并将传递设置为false。 (此操作也经过审核)。

    我的要求比上面的例子稍微复杂一点:P

    因此上述事件的顺序应为:

    1. 更新转移表
    2. 插入AuditRecord(Parent)
    3. 插入AuditRecord(Child)
    4. 插入TransferAuditRecord
    5. 插入资产负债表
    6. 插入AuditRecord(Child)
    7. 插入BalanceAuditRecord
    8. 插入资产负债表
    9. 插入AuditRecord(Child)
    10. 插入BalanceAuditRecord
    11. 但是,上面定义的级联选项在update语句中失败。 Hibernate拒绝将记录插入到多对多表中(即使AuditRecord Mapping上的unsaved-value =“any”)。我总是希望在多对多表中插入行,因此可能有一个Transfer有许多审核记录标记以前的事件。但是,最新事件确定用户想要查看的消息。 Hibernate要么尝试更新多对多表和以前的AuditRecord条目,要么只是拒绝插入AuditRecord和TransferAuditRecord,抛出一个TransientObjectException。

      审核消息的检索方式如下:

      msg=... + ((AuditRecord) balance.getAuditRecords().toArray()[getAuditRecords().size()-1])
          .getParentAuditRecord().getAuditRecordType().getDescription() + ...;
      

      该消息应该是这样的: “用户名设置转移到2008年10月11日中午12点”

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

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

在设计层面,似乎只有插入式数据库设计才能在这里实现奇迹。

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

另外,我只是研究了JBoss Envers,它看起来也很有用。