.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rgba(37, 41, 51, 1) }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { line-height: 1.5; margin-top: 35px; margin-bottom: 10px; padding-bottom: 5px }
.markdown-body h1 { font-size: 24px; line-height: 38px; margin-bottom: 5px }
.markdown-body h2 { font-size: 22px; line-height: 34px; padding-bottom: 12px; border-bottom: 1px solid rgba(236, 236, 236, 1) }
.markdown-body h3 { font-size: 20px; line-height: 28px }
.markdown-body h4 { font-size: 18px; line-height: 26px }
.markdown-body h5 { font-size: 17px; line-height: 24px }
.markdown-body h6 { font-size: 16px; line-height: 24px }
.markdown-body p { line-height: inherit; margin-top: 22px; margin-bottom: 22px }
.markdown-body img { max-width: 100% }
.markdown-body hr { border-top: 1px solid rgba(221, 221, 221, 1); border-right: none; border-bottom: none; border-left: none; margin-top: 32px; margin-bottom: 32px }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(255, 245, 245, 1); color: rgba(255, 80, 44, 1); font-size: 0.87em; padding: 0.065em 0.4em }
.markdown-body code, .markdown-body pre { font-family: Menlo, Monaco, Consolas, Courier New, monospace }
.markdown-body pre { overflow: auto; position: relative; line-height: 1.75 }
.markdown-body pre>code { font-size: 12px; padding: 15px 12px; margin: 0; word-break: normal; display: block; overflow-x: auto; color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.markdown-body a { text-decoration: none; color: rgba(2, 105, 200, 1); border-bottom: 1px solid rgba(209, 233, 255, 1) }
.markdown-body a:active, .markdown-body a:hover { color: rgba(39, 91, 140, 1) }
.markdown-body table { display: inline-block !important; font-size: 12px; width: auto; max-width: 100%; overflow: auto; border: 1px solid rgba(246, 246, 246, 1) }
.markdown-body thead { background: rgba(246, 246, 246, 1); color: rgba(0, 0, 0, 1); text-align: left }
.markdown-body tr:nth-child(2n) { background-color: rgba(252, 252, 252, 1) }
.markdown-body td, .markdown-body th { padding: 12px 7px; line-height: 24px }
.markdown-body td { min-width: 120px }
.markdown-body blockquote { color: rgba(102, 102, 102, 1); padding: 1px 23px; margin: 22px 0; border-left: 4px solid rgba(203, 203, 203, 1); background-color: rgba(248, 248, 248, 1) }
.markdown-body blockquote:after { display: block; content: "" }
.markdown-body blockquote>p { margin: 10px 0 }
.markdown-body ol, .markdown-body ul { padding-left: 28px }
.markdown-body ol li, .markdown-body ul li { margin-bottom: 0; list-style: inherit }
.markdown-body ol li .task-list-item, .markdown-body ul li .task-list-item { list-style: none }
.markdown-body ol li .task-list-item ol, .markdown-body ol li .task-list-item ul, .markdown-body ul li .task-list-item ol, .markdown-body ul li .task-list-item ul { margin-top: 0 }
.markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul { margin-top: 3px }
.markdown-body ol li { padding-left: 6px }
.markdown-body .contains-task-list { padding-left: 0 }
.markdown-body .task-list-item { list-style: none }
@media (max-width: 720px) { .markdown-body h1 { font-size: 24px } .markdown-body h2 { font-size: 20px } .markdown-body h3 { font-size: 18px } }.markdown-body pre, .markdown-body pre>code.hljs { color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.hljs-comment, .hljs-quote { color: rgba(153, 153, 136, 1); font-style: italic }
.hljs-keyword, .hljs-selector-tag, .hljs-subst { color: rgba(51, 51, 51, 1); font-weight: 700 }
.hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable { color: rgba(0, 128, 128, 1) }
.hljs-doctag, .hljs-string { color: rgba(221, 17, 68, 1) }
.hljs-section, .hljs-selector-id, .hljs-title { color: rgba(153, 0, 0, 1); font-weight: 700 }
.hljs-subst { font-weight: 400 }
.hljs-class .hljs-title, .hljs-type { color: rgba(68, 85, 136, 1); font-weight: 700 }
.hljs-attribute, .hljs-name, .hljs-tag { color: rgba(0, 0, 128, 1); font-weight: 400 }
.hljs-link, .hljs-regexp { color: rgba(0, 153, 38, 1) }
.hljs-bullet, .hljs-symbol { color: rgba(153, 0, 115, 1) }
.hljs-built_in, .hljs-builtin-name { color: rgba(0, 134, 179, 1) }
.hljs-meta { color: rgba(153, 153, 153, 1); font-weight: 700 }
.hljs-deletion { background: rgba(255, 221, 221, 1) }
.hljs-addition { background: rgba(221, 255, 221, 1) }
.hljs-emphasis { font-style: italic }
.hljs-strong { font-weight: 700 }

@[toc]

前言

近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。

本项目为前后端分离开发,后端基于Java21和SpringBoot3开发,后端使用Spring Security、JWT、Spring Data JPA等技术栈,前端提供了vue、angular、react、uniapp、微信小程序等多种脚手架工程。

在项目中每条数据在创建或修改的时候,我想记录创建人,创建时间,最后修改人,最后修改时间等审计信息。如果每次都手动赋值,代码会变得冗长,显得很不优雅。Spring Data JPA为我们提供了审计功能,可以在执行创建或修改操作时自动为审计信息赋值。

本文将介绍两种实现方式,关于SpringBoot项目如何整合Spring Data JPA,请参阅blog.csdn.net/kingslave1/…

实现方式

使用Spring Data JPA实现审计功能,主要涉及以下注解:

  • @EnableJpaAuditing,启用JPA审计功能开关。
  • @EntityListeners,可以监听实体对象的增删改查操作,调用监听器中设置的回调方法。
  • @CreatedBy,创建人,执行insert操作时自动赋值。
  • @CreatedDate,创建日期,执行insert操作时自动赋值。
  • @LastModifiedBy,最后修改人,执行insert或update操作时自动赋值。
  • @LastModifiedDate,最后修改时间,执行insert或update操作时自动赋值。

基于AuditorAware接口实现审计功能

启用JPA审计功能

定义一个配置类Bean,启用Spring Data JPA和审计功能,也可以直接main方法所在类上直接添加@EnableJpaRepositories、@EntityScan和@EnableJpaAuditing注解。

/**
* Spring Data JPA Bean配置
* 启用Jpa,扫描指定包下的Repository类和指定包下的实体类
*/
@Configuration
@EnableJpaRepositories(basePackages = {"com.demo.data.repo"})
@EntityScan(basePackages = "com.demo.data.model")
@EnableJpaAuditing
public class JpaConfig {
}

定义实体类

以定义一个用户类SysUser为例,为其添加@EntityListeners({AuditingEntityListener.class})注解,在其审计信息属性上添加@CreatedBy等注解。

@Getter
@Setter
@Entity
@EntityListeners({AuditingEntityListener.class})
public class SysUser implements Serializable {
/**
* ID,唯一标识列,使用主键自增策略
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 创建时间
*/
@CreatedDate
private LocalDateTime createdTime;
/**
* 最后修改时间
*/
@LastModifiedDate
private LocalDateTime lastModifiedTime;
/**
* 创建人ID
*/
@CreatedBy
private Long creatorId;
/**
* 最后修改人ID
*/
@LastModifiedBy
private Long lastModifierId;
/**
* 用户名
*/
@Column(unique = true)
private String username;
/**
* 密码
*/
private String password;
/**
* 电话
*/
private String phone;
}

实现AuditorAware接口

我们需要定义一个AuditorAware接口的实现类,并将其注册为Bean。

实现AuditorAware<T>接口需要传递类型参数,这个参数应使用创建人(creatorId)和最后修改人(lastModifierId)属性的类型,在实体类SysUser中creatorId和lastModifierId的类型是Long,则此处泛型参数也应该为Long。

@Component
public class AuditorAwareImpl implements AuditorAware<Long> {
/**
* 返回当前用户ID,insert和update操作会调用该方法自动赋值
*/
@Override
public Optional<Long> getCurrentAuditor() {
// 获取当前用户Id,具体获取逻辑请自行实现
Long userId = 1L;
return Optional.ofNullable(userId);
}
}

至此,审计功能开发完成,可调用SysUser类的Repository接口执行创建或修改操作来测试是否开发成功。

基于自定义监听器实现审计功能

基于AuditorAware接口实现审计功能虽然简单,但也存在不适用的场景,例如创建人属性不仅要记录创建人的用户ID,还需要记录创建人的用户名时;或者开发者希望仅在insert操作时执行某些操作。
针对上述场景,可以考虑使用自定义实体操作监听器的方式实现,实现步骤如下。

启用JPA审计功能

定义一个配置类Bean,启用Spring Data JPA和审计功能,也可以直接main方法所在类上直接添加@EnableJpaRepositories、@EntityScan和@EnableJpaAuditing注解。

/**
* Spring Data JPA Bean配置
* 启用Jpa,扫描指定包下的Repository类和指定包下的实体类
*/
@Configuration
@EnableJpaRepositories(basePackages = {"com.demo.data.repo"})
@EntityScan(basePackages = "com.demo.data.model")
@EnableJpaAuditing
public class JpaConfig {
}

定义实体类

以定义一个用户类SysUser为例,为其添加@EntityListeners({AuditingEntityListener.class, CustomEntityAuditingListener.class})注解,在其审计信息属性上添加@CreatedBy等注解。
与1.2一节中给出的实体类相比,新增了creator,lastModifier两个属性,@EntityListeners注解中多了一个CustomEntityAuditingListener,在下一节中笔者将给出CustomEntityAuditingListener的实现代码。

@Getter
@Setter
@Entity
@EntityListeners({AuditingEntityListener.class, CustomEntityAuditingListener.class})
public class SysUser implements Serializable {
/**
* ID,唯一标识列,使用主键自增策略
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 创建时间
*/
@CreatedDate
private LocalDateTime createdTime;
/**
* 最后修改时间
*/
@LastModifiedDate
private LocalDateTime lastModifiedTime;
/**
* 创建人ID
*/
@CreatedBy
private Long creatorId;
/**
* 创建人用户名
*/
private String creator;
/**
* 最后修改人ID
*/
@LastModifiedBy
private Long lastModifierId;
/**
* 最后修改人用户名
*/
private Long lastModifier;
/**
* 用户名
*/
@Column(unique = true)
private String username;
/**
* 密码
*/
private String password;
/**
* 电话
*/
private String phone;
}

定义自定义监听器类

自定义监听器会用到以下几种注解,可以监听数据库操作的不同时机。

  • @PostLoad,实体对象查询之后
  • @PrePersist,实体对象保存之前
  • @PostPersist,实体对象保存之后
  • @PreUpdate,实体对象修改之前
  • @PostUpdate,实体对象修改之后
  • @PreRemove,实体对象删除之前
  • @PostRemove,实体对象删除之后

以下是CustomEntityAuditingListener类的实现代码,使用了@PrePersist和@PreUpdate两个注解。

public class CustomEntityAuditingListener {
@PrePersist
private void prePersist(BaseEntity entity) {
// 获取当前用户,具体获取逻辑请自行实现
SysUser current = new SysUser(); entity.setCreatorId(current.getId());
entity.setCreator(current.getUsername());
entity.setLastModifierId(current.getId());
entity.setLastModifier(current.getUsername());
} @PreUpdate
private void preUpdate(BaseEntity entity) {
// 获取当前用户,具体获取逻辑请自行实现
SysUser current = new SysUser(); entity.setLastModifierId(current.getId());
entity.setLastModifier(current.getUsername());
}
}

至此,审计功能开发完成,可调用SysUser类的Repository接口执行创建或修改操作来测试是否开发成功。

总结

本文介绍了两种使用Spring Data JPA实现审计功能的方法,实现AuditorAware接口和自定义实体操作监听器,可以在创建或修改数据时自动为审计信息赋值,减少了冗长的手动赋值代码,如有错误,还望批评指正。

在后续实践中我也是及时更新自己的学习心得和经验总结,希望与诸位看官一起进步。

使用Spring Data JPA实现审计功能,记录创建人、创建时间、最后修改时间和最后修改人的更多相关文章

  1. Spring Data JPA的Audit功能,审计数据库的变更

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 数据库审计 数据库审计是指当数据库有记录变更时,可以记录数据库的变更时间和变更人等,这样以后出问题回溯问责也比较方便. ...

  2. spring data jpa auditing

    审计日志,记录实体版本的修改信息,创建时间,修改时间,创建人,修改人等 可以采用切片AOP的方式实现,也可以通过Spring Data JPA的审计功能实现 切片方式 Spring AOP中Point ...

  3. Spring Data JPA(官方文档翻译)

    关于本书 介绍 关于这本指南 第一章 前言 第二章 新增及注意点 第三章 项目依赖 第四章 使用Spring Data Repositories 4.1 核心概念 4.2 查询方法 4.3 定义rep ...

  4. Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!

    前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...

  5. spring data jpa自定义baseRepository

    在一些特殊时候,我们会设计到对Spring Data JPA中的方法进行重新实现,这将会面临一个问题,如果我们新创建一个实现类.如果这个实现类实现了JpaRepository接口,这样我们不得不实现该 ...

  6. 12 Spring Data JPA:springDataJpa的运行原理以及基本操作(上)

    spring data jpaday1:orm思想和hibernate以及jpa的概述和jpa的基本操作 day2:springdatajpa的运行原理 day2:springdatajpa的基本操作 ...

  7. Spring data jpa 使用技巧记录

    软件152 尹以操 最近在用Springboot 以及Spring data jpa  ,使用jpa可以让我更方便的操作数据库,特开此帖记录使用jpa的一些小技巧. 一.使用spring data j ...

  8. Spring Data JPA 学习记录1 -- 单向1:N关联的一些问题

    开新坑 开新坑了(笑)....公司项目使用的是Spring Data JPA做持久化框架....学习了一段时间以后发现了一点值得注意的小问题.....与大家分享 主要是针对1:N单向关联产生的一系列问 ...

  9. SpringBoot学习笔记:Spring Data Jpa的使用

    更多请关注公众号 Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR ...

  10. spring data jpa入门学习

    本文主要介绍下spring data jpa,主要聊聊为何要使用它进行开发以及它的基本使用.本文主要是入门介绍,并在最后会留下完整的demo供读者进行下载,从而了解并且开始使用spring data ...

随机推荐

  1. .NET周刊【12月第1期 2023-12-06】

    国内文章 .NET 与 OpenEuler 共展翅,昇腾九万里 https://www.cnblogs.com/shanyou/p/17858385.html 本文介绍了openEuler操作系统,它 ...

  2. 华企盾DSC防泄密系统造成应用程序卡慢、编译卡问题

    1.先看看个人模式是否正常,正常则跟进程有关加密nofile.不启用进程水印.不启用文件夹大小缓存(源码文件去掉需慎重)都关掉.允许进程间访问(procmon排查是否有其它进程访问) 2.检查是否与H ...

  3. NTP时间服务器优先级介绍

    先思考一个问题:当一个客户端配置向多个NTP时间服务器校时,此时客户端优先向哪个时间服务器同步时间呢? 一个完整的NTP校时请求分四步: 1.客户端向服务器发起校时请求 2.服务器收到客户端发送的校时 ...

  4. 半小时实现GPT纯血鸿蒙版

    仅需半小时,即可实现纯血鸿蒙版本的ChatGPT! 废话少说,先看效果图: 如上图所示,这个小Demo实现了AI智能问答.靠右加粗的文本是用户点击底部提交按钮后出现的:后面靠左对齐的普通文本是来自AI ...

  5. 新版以太坊Ethereum库ethersV5.0配合后端Golang1.18实时链接区块链钱包(Metamask/Okc)以及验签操作

    区块链去中心化思想无处不在,比如最近使用个体抗原自检替代大规模的中心化核酸检测,就是去中心化思想的落地实践,避免了大规模聚集导致的交叉感染,提高了检测效率,本次我们使用Ethereum最新的ether ...

  6. 伯克利:serverless是下一代计算范式

    摘要:Serverless技术正是云厂商的基于规模经济的一个选择. 引子 刚过去的HC2020,华为面向多样化算力的时代,发布了DC分布式计算的三个开发套件,其中一个是元戎组件.元戎是基于函数计算的分 ...

  7. Redisson:这么强大的实现分布式锁框架,你还没有?

    摘要:Redisson框架十分强大,基于Redisson框架可以实现几乎你能想到的所有类型的分布式锁. 本文分享自华为云社区<[高并发]你知道吗?大家都在使用Redisson实现分布式锁了!!& ...

  8. IOS证书制作教程

    ​ 转载:IOS证书制作教程 点击苹果证书 按钮 ​ 编辑 点击新增 ​ 编辑 输入证书密码,名称 这个密码不是账号密码,而是一个保护证书的密码,是p12文件的密码,此密码设置后没有其他地方可以找到, ...

  9. SQL Server 2016 安装

    数据库安装 选择全新安装模式继续安装 输入产品秘钥:这里使用演示秘钥进行 接受许可 规则检测 可以后期再开放防火墙对外端口 选择需要安装的功能,想省事可以选择[全选] 可以安装JDK,这边选择取消 P ...

  10. MongoDB 读写分离——SpringBoot读写分离

    application.yml data: mongodb: uri: mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/fecg_d ...