当前位置: 首页 > 知识库问答 >
问题:

如何使用Spring Data JPA的审计功能在历史表上指示有效日期?

瞿宏儒
2023-03-14

我有一个带名称列的Products表,并希望在Product历史表中维护名称更改的历史记录。(Products表还有其他一些列。我不需要跟踪这些代码的数据更改。)

用户请求运行给定日期的报告时,我的应用程序将使用ProductHistory表。报告需要显示截至报告日期的产品名称。

我使用的是Spring Boot 1.5.2,包括Spring Boot的Starter数据JPA依赖项。我计划在Spring Data JPA中使用Hibernate Envers来帮助实现我的目标。我已经阅读了此文档,但不确定如何在产品历史记录上指定生效日期。

我已启用JPA审核:

@Configuration
@EnableJpaRepositories("com.example.repository")
@EnableJpaAuditing
public class DatabaseConfig {
    //config items
}

使用文档作为参考,我应该将实体设置为:

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @LastModifiedDate
    private Date effectiveDateUtc;

    private String name;

    //other fields corresponding to othercols
 }

但是effecteDateUtc不是Products的字段/列。如何表明effecteDateUtcProduct历史的属性?

更新

我一直无法找到一种使用Spring Data JPA的审计功能来完成上述任务的方法。不过,使用Naros/Hibernate envers发布的答案,我能够实现我的目标。

除了Naros发布的内容之外,以下是我发现我还需要做些什么才能让它与我的Spring应用程序一起工作:

将所需的罐子添加到我的gradle构建中:

compile('org.hibernate:hibernate-envers:5.2.10.Final')
compile('org.hibernate:hibernate-core:5.2.10.Final')
compile('joda-time:joda-time:2.1')

在DB中创建必要的架构。

我还设置了一个集成测试用例,以验证我可以对修订数据执行报告类型查询。为此,我需要找到实体经理并将其传递给audit reader工厂

@RunWith(SpringRunner.class)
@SpringBootTest(classes=ProductDataApplication.class)
public class ProductRepositoryTest {

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    @Transactional
    public void testEnversQuerying() {
        AuditReader ar = AuditReaderFactory.get(entityManager);
        Product revisions = ar.find(Product.class, 1L, new Date());
        // assert code
    }
}

正如Naros推测的那样,我还需要存储与修订版关联的用户id。我是这样做的:

public class UserRevisionListener implements RevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {
        UserRevision revision = (UserRevision) revisionEntity;

        if (SecurityContextHolder.getContext().getAuthentication() == null ||
                SecurityContextHolder.getContext().getAuthentication().getName() == null) {
            revision.setModifiedBy("system");
        } else {
            revision.setModifiedBy(SecurityContextHolder.getContext().getAuthentication().getName());
        }

    }
} 

共有1个答案

顾光明
2023-03-14

我意识到您可能正在寻找与Spring相关的响应;然而,如果您直接使用HiberNate Envers,我至少可以向您提供如何工作的详细信息。

>

  • 设置实体:

    @Entity
    public class Product {
      @Id
      private Integer id;
      @Audited
      private String name;
      // other attributes
    }
    

    正如你所见,我们基本上是说,我们只是想审计产品的名称。您在实体中映射的任何其他属性都将被忽略,因为该类没有使用@Audited注释,并且我们专门针对属性name

    与您所展示的Product历史模型相反,Envers实际上将为您创建两个表。第一个是Product_AUD表,我们将在其中存储对Products实体所做更改的审计日志。

    Product_AUD表将包含与以下内容非常相似的列:

    REV INT NOT NULL         <- Foreign Key to the Revision Entity Table (REVINFO)
    PRODUCT_ID INT NOT NULL  <- Your PK from your Product Table
    NAME VARCHAR(255)        <- The name column annotated with @Audited
    REVTYPE INT              <- Revision type (0=INSERT,1=UPDATE,2=DELETE)
    

    下一个要构建的表将是REVINFO。默认情况下,此表仅由两列构成,看起来如下所示:

    REV INT NOT NULL         <- The revision number (PK)
    REVTSTMP BIGINT          <- The time in milliseconds
    

    Envers以这种方式构建模型的原因是,在一个自事务中修改多个实体并不少见,我们没有在所有实体审计表中复制许多特定于修订版的属性,而是以规范化的方式将其合并,并将数据存储在REVINFO表中。

    你可能想要的一件事似乎是你倾向于直接使用Spring Data,那就是能够捕获用户以及与修订发生时的环境相关的其他属性。你很幸运,因为Envers也提供了一种快速而无痛苦的方法来做到这一点。

    >

    @Entity
    @Table(name = "REVINFO")
    @RevisionEntity(MyRevisionEntityListener.class)
    public class MyRevisionEntity {
      @Id
      @RevisionNumber
      @Column(name = "REV", nullable = false, updatable = false)
      private Integer id;
      @RevisionTimestamp
      @Column(name = "REVTSTMP", nullable = false, updatable = false)
      private Long timestamp;
      @Column(name = "MODIFIED_BY", length = 100)
      private String modifiedBy;
      // getter/setters and perhaps other attributes
    }
    

    定义用于在修订实体实例上填充自定义属性的修订实体侦听器:

    class MyRevisionEntityListener implements RevisionListener {
      @Override
      public void newRevision(Object revisionEntity) {
      }
    }
    

    在这个监听器中,您需要做的是获取想要注入到Envers修订实体中的任何上下文信息,并将其设置到您的修订实体类中。

    将信息存储到此侦听器中的一种方法是将其存储在线程本地变量中,并在侦听器回调中访问该变量。如果您使用Spring Security作为示例,它已经在其SecurityContextHolder线程本地容器中为您完成了此操作。如果您使用其他方法,您可以简单地以它们的实现为例。

    正如我最初提到的,这实际上并没有直接解决您的Spring数据需求,但它确实说明了如果您决定直接使用Envers,它是如何发生的。

  •  类似资料:
    • 我有一组用于应用程序配置的域对象及其相关表。经过验证的用户可以通过表示层更改这些域对象数据。这些域对象有非常重要的数据,我需要找到谁和何时改变了他们的数据。我的应用程序的数据访问层是使用JPA、Hibernate和Spring实现的。我需要有每个变化的记录,包括:用户操作日期操作类型以前的值。 例如,让我们考虑一个简单的域对象(简化为这个问题的目的): 假设存在具有以下值的实例: 用户(如John

    • 我正在尝试使用谷歌日历API创建谷歌日历事件。具体来说,我使用的是服务帐户方法,因为我的应用程序将运行一系列事件来创建并将它们插入我的谷歌日历。使用的语言是PHP。 我已经从gitHub下载了PHP客户端库。https://github.com/googleapis/google-api-php-client. 我的测试代码在下面。这成功地完成了,并给我回了一个长的事件标识。然而,当我尝试将其粘贴

    • 为了分析系统上的高危行为,能够尽可能的对危险行为提前预警,G01会对系统的进程行为和流量进行监控和过滤。同时在一些特殊情况下,可以通过开关将功能进行bypass操作,便于快速解决问题。

    • 我随时都有一个“用户”表,如果插入、更新或删除了一行,那么我需要在“用户审核”表中插入一行,其中包含所有用户数据和操作列,以记录执行的操作。 我用的是Spring批处理,Spring数据JPA把数据插入用户表 我如何使用SpringDataJPA在SpringBatch中实现这一点,或者JPA/hibernate是否提供了任何实现?

    • 本文向大家介绍Android实现搜索历史功能,包括了Android实现搜索历史功能的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了Android实现搜索历史的具体代码,供大家参考,具体内容如下 SharedPreferences实现本地搜索历史功能,覆盖搜索重复的文本,可清空  1. 判断搜索内容是否含表情,不需要可以不判断 2.软键盘工具类弹出、关闭,不需要可以不判断 3.存储工具

    • 本文向大家介绍基于Spring Data的AuditorAware审计功能的示例代码,包括了基于Spring Data的AuditorAware审计功能的示例代码的使用技巧和注意事项,需要的朋友参考一下 Spring Data提供支持审计功能:即由谁在什么时候创建或修改实体。Spring Data提供了在实体类的属性上增加@CreatedBy,@LastModifiedBy,@CreatedDat