hibernate-envers保姆级实战指南

介绍

Hibernate Envers,是一个审计和历史记录框架,提供实时数据审计,它提供了一些必要的基本功能,允许您跟踪实体实例的变化,可以检索这些变化,以及由这些变化引起的变化。

Hibernate Envers是在Hibernate ORM基础上构建的一个扩展,它可以捕获实体类中属性值变形 的审计信息,并可以提供查看特定历史数据的功能,支持灵活的审计策略。例如,用户可以根据时间戳记录用户访问或数据修改时间,也可以根据某个特定用户记录数据变形信息。Hibernate Envers还可以基于审计策略设置自定义的前、后事件,以实现等级审计。 ------- from ChatGPT

环境配置

测试基于SpringBoot+JPA+H2进行,相关依赖如下

	    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-envers</artifactId>
            <version>6.1.7.Final</version>
        </dependency>

配置文件如下

server:
  port: 8080

spring:
  application:
    name: hibernate-enver-test
  datasource:
    url: jdbc:h2:file:~/test;NON_KEYWORDS=USER;OLD_INFORMATION_SCHEMA=TRUE;AUTO_SERVER=TRUE
    username: test
    password: test
    driver-class-name: org.h2.Driver
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

代码准备

首先是启动类,因为要使用自动审计功能维护时间字段所以添加了@EnableJpaAuditing注解

@EnableJpaAuditing
@SpringBootApplication
public class StartApplication {

    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class, args);
    }

}

接下来创建User实体,在类上添加@Audited代表需要让envers托管整个类,并据此生成变更记录。

@Entity
@Getter
@Setter
@Builder
@Audited
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @CreatedDate
    private LocalDateTime createTime;

    @LastModifiedDate
    private LocalDateTime updateTime;

}

我们也可以只针对某些字段做审计,例如在此例中我们只关心id和name。

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @Audited
    @GeneratedValue
    private Long id;

    @Audited
    private String name;

    @CreatedDate
    private LocalDateTime createTime;

    @LastModifiedDate
    private LocalDateTime updateTime;

}

最后创建相关controller、service与repo。

@RestController
@RequiredArgsConstructor
@RequestMapping("users")
public class UserController {

    private final UserService userService;

    @PostMapping("{name}")
    public void createUser(@PathVariable String name) {
        userService.createUser(name);
    }

    @PutMapping("{id}/{name}")
    public void updateUser(@PathVariable Long id, @PathVariable String name) {
        userService.updateUser(id, name);
    }


    @DeleteMapping("{id}")
    public void updateUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @GetMapping("{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }

}

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;


    public void createUser(String name) {
        userRepository.save(User.builder().name(name).build());
    }

    public void updateUser(Long id, String name) {
        userRepository.findById(id).ifPresent(
                user-> {
                    user.setName(name);
                    userRepository.save(user);
                }
        );
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow(()-> new RuntimeException("user not exist."));
    }
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {


}

测试

启动项目后,我们先来看一下表结构,可以发现生成了以下三个表,其中USER为我们业务表,REVINFO与USER_AUD则是envers自动生成的审计记录表。
在这里插入图片描述
REVINFO表结构如下其中REV为主键id, REVSTAMP是创建时间

create table REVINFO
(
    REV      INTEGER auto_increment
        primary key,
    REVTSTMP BIGINT
);

接下来是我们的USER表

create table USER
(
    ID          BIGINT not null
        primary key,
    CREATE_TIME TIMESTAMP(6, 0),
    NAME        CHARACTER,
    UPDATE_TIME TIMESTAMP(6, 0)
);

USER_AUD是USER表的审计表其中ID和NAME为我们的待审计字段,REV为REVINFO的ID也是此表的外键,REVTYPE是修改类型(0表示新增,1表示更新,2表示删除)

create table USER_AUDIT
(
    ID            BIGINT  not null,
    REV   		  INTEGER not null,
    REVISION_TYPE TINYINT,
    NAME          CHARACTER,
    primary key (ID, REV),
    constraint FK1E6UW6EK1MH3FLETHFN1VLY35
        foreign key (REV) references REVINFO
);

我们调用接口添加一个用户Jack
在这里插入图片描述

可以看到我们的新增数据已经成功生成审计记录

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
当我们把调用更新接口将名称从Jack改成Rose后,也能对应新增一条变更记录
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
删除同理,不过可以发现删除时,审计表中没有记录名称删除的值
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

进阶

envers支持一些进阶的配置以适配各种场景

配置项含义
audit_table_prefix审计表名前缀
audit_table_suffix(默认为_AUD)审计表名后缀
revision_field_name(默认为REV)审计表中修订号的字段名称
revision_type_field_name(默认为REVTYPE)审计表中修改类型的字段名称
store_data_at_delete删除时是否保存对应值

我们对应做如下调整

  jpa:
    properties:
      org:
        hibernate:
          envers:
            audit_table_suffix: _audit
            revision_field_name: revision_id
            revision_type_field_name: revision_type
            store_data_at_delete: true

重启服务后效果如下

create table USER_AUDIT
(
    ID            BIGINT  not null,
    REVISION_ID   INTEGER not null,
    REVISION_TYPE TINYINT,
    NAME          CHARACTER,
    primary key (ID, REVISION_ID),
    constraint FK1E6UW6EK1MH3FLETHFN1VLY35
        foreign key (REVISION_ID) references REVINFO
);

删除后也能正确记录字段值
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. Hibernate 是一种: A. ORM框架 B. MVC框架 C. AOP框架 D. IOC框架 答案:A 2. MyBatis 是一种: A. ORM框架 B. MVC框架 C. AOP框架 D. IOC框架 答案:A 3. MyBatis-Plus 是一种: A. ORM框架 B. MVC框架 C. AOP框架 D. IOC框架 答案:A 4. Hibernate 支持的数据库类型包括: A. MySQL B. Oracle C. Microsoft SQL Server D. 所有主流数据库 答案:D 5. MyBatis 支持的数据库类型包括: A. MySQL B. Oracle C. Microsoft SQL Server D. 所有主流数据库 答案:D 6. MyBatis-Plus 支持的数据库类型包括: A. MySQL B. Oracle C. Microsoft SQL Server D. 所有主流数据库 答案:D 7. Hibernate 的核心特性包括: A. ORM映射 B. 缓存管理 C. 事务管理 D. 所有选项都是 答案:D 8. MyBatis 的核心特性包括: A. SQL映射 B. 缓存管理 C. 事务管理 D. 所有选项都是 答案:D 9. MyBatis-Plus 的核心特性包括: A. 自动代码生成 B. 缓存管理 C. 事务管理 D. 所有选项都是 答案:D 10. Hibernate 的二缓存机制是基于: A. 内存 B. Redis C. Memcached D. Ehcache 答案:D 11. MyBatis 的二缓存机制是基于: A. 内存 B. Redis C. Memcached D. Ehcache 答案:D 12. MyBatis-Plus 的二缓存机制是基于: A. 内存 B. Redis C. Memcached D. Ehcache 答案:D 13. Hibernate 的一缓存机制是基于: A. 内存 B. Redis C. Memcached D. Ehcache 答案:A 14. MyBatis 的一缓存机制是基于: A. 内存 B. Redis C. Memcached D. Ehcache 答案:A 15. MyBatis-Plus 的一缓存机制是基于: A. 内存 B. Redis C. Memcached D. Ehcache 答案:A 16. Hibernate 的事务管理支持: A. 编程式事务管理 B. 声明式事务管理 C. 两种都支持 D. 都不支持 答案:C 17. MyBatis 的事务管理支持: A. 编程式事务管理 B. 声明式事务管理 C. 两种都支持 D. 都不支持 答案:A 18. MyBatis-Plus 的事务管理支持: A. 编程式事务管理 B. 声明式事务管理 C. 两种都支持 D. 都不支持 答案:A 19. Hibernate 的主键生成策略包括: A. 自增长 B. UUID C. 序列 D. 所有选项都是 答案:D 20. MyBatis 的主键生成策略包括: A. 自增长 B. UUID C. 序列 D. 所有选项都是 答案:D 21. MyBatis-Plus 的主键生成策略包括: A. 自增长 B. UUID C. 序列 D. 所有选项都是 答案:D 22. Hibernate 的延迟加载机制是通过: A. 代理模式 B. 动态代理模式 C. CGLIB D. 所有选项都是 答案:A 23. MyBatis 的延迟加载机制是通过: A. 代理模式 B. 动态代理模式 C. CGLIB D. 所有选项都是 答案:A 24. MyBatis-Plus 的延迟加载机制是通过: A. 代理模式 B. 动态代理模式 C. CGLIB D. 所有选项都是 答案:A 25. Hibernate 的动态 SQL 生成是通过: A. HQL B. Criteria API C. SQL D. 所有选项都是 答案:B 26. MyBatis 的动态 SQL 生成是通过: A. HQL B. Criteria API C. SQL D. 所有选项都是 答案:C 27. MyBatis-Plus 的动态 SQL 生成是通过: A. HQL B. Criteria API C. SQL D. 所有选项都是 答案:B 28. Hibernate 的优点包括: A. 易于学习 B. 代码量少 C. 易于维护 D. 所有选项都是 答案:C 29. MyBatis 的优点包括: A. 易于学习 B. 代码量少 C. 易于维护 D. 所有选项都是 答案:A 30. MyBatis-Plus 的优点包括: A. 易于学习 B. 代码量少 C. 易于维护 D. 所有选项都是 答案:B 31. Hibernate 的缺点包括: A. 性能问题 B. 学习曲线陡峭 C. 代码量大 D. 所有选项都是 答案:A 32. MyBatis 的缺点包括: A. 性能问题 B. 学习曲线陡峭 C. 代码量大 D. 所有选项都是 答案:B 33. MyBatis-Plus 的缺点包括: A. 性能问题 B. 学习曲线陡峭 C. 代码量大 D. 所有选项都是 答案:C 34. 在 Hibernate 中,如何配置 ORM 映射? A. 使用注解 B. 使用 XML 文件 C. 两种都可 D. 都不可 答案:C 35. 在 MyBatis 中,如何配置 SQL 映射? A. 使用注解 B. 使用 XML 文件 C. 两种都可 D. 都不可 答案:B 36. 在 MyBatis-Plus 中,如何配置 SQL 映射? A. 使用注解 B. 使用 XML 文件 C. 两种都可 D. 都不可 答案:C 37. 在 Hibernate 中,如何进行复杂查询? A. 使用 HQL B. 使用 SQL C. 使用 Criteria API D. 所有选项都是 答案:D 38. 在 MyBatis 中,如何进行复杂查询? A. 使用 HQL B. 使用 SQL C. 使用 Criteria API D. 所有选项都是 答案:B 39. 在 MyBatis-Plus 中,如何进行复杂查询? A. 使用 HQL B. 使用 SQL C. 使用 Criteria API D. 所有选项都是 答案:C 40. 在 Hibernate 中,如何配置一对多关系? A. 使用 @OneToMany 注解 B. 在 XML 文件中配置 C. 两种都可 D. 都不可 答案:C 41. 在 MyBatis 中,如何配置一对多关系? A. 使用 @OneToMany 注解 B. 在 XML 文件中配置 C. 两种都可 D. 都不可 答案:B 42. 在 MyBatis-Plus 中,如何配置一对多关系? A. 使用 @OneToMany 注解 B. 在 XML 文件中配置 C. 两种都可 D. 都不可 答案:A 43. 在 Hibernate 中,如何配置多对多关系? A. 使用 @ManyToMany 注解 B. 在 XML 文件中配置 C. 两种都可 D. 都不可 答案:C 44. 在 MyBatis 中,如何配置多对多关系? A. 使用 @ManyToMany 注解 B. 在 XML 文件中配置 C. 两种都可 D. 都不可 答案:B 45. 在 MyBatis-Plus 中,如何配置多对多关系? A. 使用 @ManyToMany 注解 B. 在 XML 文件中配置 C. 两种都可 D. 都不可 答案:A 46. 在 Hibernate 中,如何进行分页查询? A. 使用 HQL B. 使用 SQL C. 使用 Criteria API D. 所有选项都是 答案:D 47. 在 MyBatis 中,如何进行分页查询? A. 使用 HQL B. 使用 SQL C. 使用 Criteria API D. 所有选项都是 答案:B 48. 在 MyBatis-Plus 中,如何进行分页查询? A. 使用 HQL B. 使用 SQL C. 使用 Criteria API D. 所有选项都是 答案:C 49. 在 Hibernate 中,如何配置多数据源? A. 使用 Spring Data JPA B. 使用 Hibernate Envers C. 使用 Spring Boot D. 所有选项都不是 答案:C 50. 在 MyBatis 中,如何配置多数据源? A. 使用 Spring Data JPA B. 使用 MyBatis Generator C. 使用 Spring Boot D. 所有选项都不是 答案:C

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值