使用Spring Data JPA实现审计功能,记录创建人、创建时间、最后修改时间和最后修改人

本文详细介绍了在SpringBoot项目中利用SpringDataJPA和两种方式实现审计功能:通过`AuditorAware`接口和自定义监听器,自动记录数据创建人、修改人信息。
摘要由CSDN通过智能技术生成

前言

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

本项目为前后端分离开发,后端基于Java21SpringBoot3开发,后端使用Spring SecurityJWTSpring Data JPA等技术栈,前端提供了vueangularreactuniapp微信小程序等多种脚手架工程。

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

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

实现方式

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

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

基于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)属性的类型,在实体类SysUsercreatorIdlastModifierId的类型是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一节中给出的实体类相比,新增了creatorlastModifier两个属性,@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接口和自定义实体操作监听器,可以在创建或修改数据时自动为审计信息赋值,减少了冗长的手动赋值代码,如有错误,还望批评指正。

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

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
很抱歉,我无法为您提供完整的源代码,但我可以给您提供一些指导和建议以帮助您开始。 首先,您需要设置一个Spring Boot项目并添加所需的依赖项。您可能需要以下依赖项: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </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> </dependencies> ``` 接下来,您需要创建一个简单的联系人类,如下所示: ```java @Data @Entity public class Contact { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank private String name; @Email @NotBlank private String email; @NotBlank private String phone; public Contact() {} public Contact(String name, String email, String phone) { this.name = name; this.email = email; this.phone = phone; } } ``` 然后,您需要创建一个联系人存储库接口,如下所示: ```java @Repository public interface ContactRepository extends JpaRepository<Contact, Long> {} ``` 接下来,您需要创建一个联系人服务接口和实现,如下所示: ```java public interface ContactService { List<Contact> getAllContacts(); void saveContact(Contact contact); void deleteContact(Long id); Contact getContactById(Long id); } @Service public class ContactServiceImpl implements ContactService { @Autowired private ContactRepository contactRepository; @Override public List<Contact> getAllContacts() { return contactRepository.findAll(); } @Override public void saveContact(Contact contact) { contactRepository.save(contact); } @Override public void deleteContact(Long id) { contactRepository.deleteById(id); } @Override public Contact getContactById(Long id) { Optional<Contact> optionalContact = contactRepository.findById(id); return optionalContact.orElse(null); } } ``` 然后,您需要创建一个控制器类,如下所示: ```java @Controller public class ContactController { @Autowired private ContactService contactService; @GetMapping("/") public String home(Model model) { List<Contact> contacts = contactService.getAllContacts(); model.addAttribute("contacts", contacts); return "home"; } @GetMapping("/add") public String addContactForm(Model model) { model.addAttribute("contact", new Contact()); return "add_contact"; } @PostMapping("/save") public String saveContact(@ModelAttribute("contact") Contact contact) { contactService.saveContact(contact); return "redirect:/"; } @GetMapping("/edit/{id}") public String editContactForm(@PathVariable Long id, Model model) { Contact contact = contactService.getContactById(id); model.addAttribute("contact", contact); return "edit_contact"; } @PostMapping("/update/{id}") public String updateContact(@PathVariable Long id, @ModelAttribute("contact") Contact contact) { Contact existingContact = contactService.getContactById(id); existingContact.setName(contact.getName()); existingContact.setEmail(contact.getEmail()); existingContact.setPhone(contact.getPhone()); contactService.saveContact(existingContact); return "redirect:/"; } @GetMapping("/delete/{id}") public String deleteContact(@PathVariable Long id) { contactService.deleteContact(id); return "redirect:/"; } } ``` 最后,您需要创建一些前端页面,如下所示: home.html ```html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Contacts</title> </head> <body> <h1>Contacts</h1> <table> <tr> <th>Name</th> <th>Email</th> <th>Phone</th> <th></th> <th></th> </tr> <tr th:each="contact : ${contacts}"> <td th:text="${contact.name}"></td> <td th:text="${contact.email}"></td> <td th:text="${contact.phone}"></td> <td><a th:href="@{/edit/{id}(id=${contact.id})}">Edit</a></td> <td><a th:href="@{/delete/{id}(id=${contact.id})}" onclick="return confirm('Are you sure?')">Delete</a></td> </tr> </table> <p><a href="/add">Add Contact</a></p> </body> </html> ``` add_contact.html ```html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Add Contact</title> </head> <body> <h1>Add Contact</h1> <form th:action="@{/save}" th:object="${contact}" method="post"> <label for="name">Name:</label> <input type="text" id="name" name="name" required th:value="${contact.name}"/><br/> <label for="email">Email:</label> <input type="email" id="email" name="email" required th:value="${contact.email}"/><br/> <label for="phone">Phone:</label> <input type="text" id="phone" name="phone" required th:value="${contact.phone}"/><br/> <input type="submit" value="Save"/> </form> </body> </html> ``` edit_contact.html ```html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Edit Contact</title> </head> <body> <h1>Edit Contact</h1> <form th:action="@{/update/{id}(id=${contact.id})}" th:object="${contact}" method="post"> <label for="name">Name:</label> <input type="text" id="name" name="name" required th:value="${contact.name}"/><br/> <label for="email">Email:</label> <input type="email" id="email" name="email" required th:value="${contact.email}"/><br/> <label for="phone">Phone:</label> <input type="text" id="phone" name="phone" required th:value="${contact.phone}"/><br/> <input type="submit" value="Save"/> </form> </body> </html> ``` 这只是一个简单的示例,但希望它能帮助您开始使用Spring框架创建通讯录应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偏安zzcoder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值