学习Spring-Data-Jpa(十五)---Auditing与@MappedSuperclass
1、Auditing
一般我们针对一张表的操作需要记录下来,是谁修改的,修改时间是什么,Spring-Data为我们提供了支持。
1.1、在实体类中使用Spring-Data为我们提供的四个注解(也可以选择实现Auditable接口或继承AbstractAuditable类,推荐使用注解)
1.2、在实体上添加@EntityListeners(value = AuditingEntityListener.class)启动对当前实体的监听。
- /**
- * 测试spring-data为我们提供的审计功能
- *
- * @author caofanqi
- */
- @Data
- @Entity
- @Builder
- @Table(name = "jpa_audit_user")
- @NoArgsConstructor
- @AllArgsConstructor
- @EntityListeners(value = AuditingEntityListener.class)
- public class AuditUser {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @Column(unique = true)
- private String name;
- @CreatedDate
- private LocalDateTime createdDate;
- @LastModifiedDate
- private LocalDateTime lastModifiedDate;
- @CreatedBy
- @ManyToOne
- private AuditUser createdBy;
- @LastModifiedBy
- @ManyToOne
- private AuditUser lastModifiedBy;
- }
1.3、如果在实体中使用了@CreatedBy或者@LastModifiedBy需要实现AuditorAware<T>接口,告诉Spring-Data当前审计用户是谁。(一般项目中从spring security或token中获取)
- /**
- * 获取当前的审计人,实际项目中可以从Spring Security中或Token/{session}中获取,这里只是举个例子进行模拟。
- * @author caofanqi
- */
- public class AuditorAwareImpl implements AuditorAware<AuditUser> {
- private Optional<AuditUser> currentUser = Optional.empty();
- public void setCurrentUser(AuditUser currentUser){
- this.currentUser = Optional.of(currentUser);
- }
- @Override
- public Optional<AuditUser> getCurrentAuditor() {
- //要使用的当前用户
- return currentUser;
- }
- }
1.4、在启动类上添加@EnableJpaAuditing启动审计功能。
1.5、如果ApplicationContext中只有一个AuditorAware类型的bean,Spring-Date会自动选择,如果又多个,需要通过@EnableJpaAuditing注解的auditorAwareRef属性进行设置。
- /**
- * 启动类
- * @author caofanqi
- */
- @SpringBootApplication
- @EnableAsync
- @EnableJpaRepositories(
- /*queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND*/
- /* ,repositoryImplementationPostfix = "MyPostfix",*/
- /*repositoryBaseClass = MyRepositoryImpl.class*/)
- @EnableJpaAuditing
- public class StudySpringDataJpaApplication {
- public static void main(String[] args) {
- SpringApplication.run(StudySpringDataJpaApplication.class, args);
- }
- /**
- * 如果ApplicationContext中只有一个AuditorAware类型的bean,Spring-Date会自动选择,
- * 如果又多个,需要通过@EnableJpaAuditing注解的auditorAwareRef属性进行设置。
- */
- @Bean
- public AuditorAware<AuditUser> auditorProvider() {
- return new AuditorAwareImpl();
- }
- }
1.6、测试用例,及生成的表
- @SpringBootTest
- class AuditUserRepositoryTest {
- @Resource
- private AuditUserRepository auditUserRepository;
- @Resource
- private AuditorAwareImpl auditorAware;
- @Test
- void testAuditDate(){
- /*
- *不设置创建和修改时间,由springl-data替我们完成
- */
- AuditUser audit = AuditUser.builder().name("张三").build();
- AuditUser save = auditUserRepository.save(audit);
- System.out.println(save);
- }
- @Test
- void testAuditUser(){
- /*
- * 模拟当前用户
- */
- auditorAware.setCurrentUser(auditUserRepository.findByName("张三"));
- /*
- * 这里不设置是谁保存的,看spring-data是否会为我们完成
- */
- AuditUser audit = AuditUser.builder().name("李四").build();
- AuditUser save = auditUserRepository.save(audit);
- System.out.println(save);
- }
- }
testAuditDate控制台打印:
testAuditUser控制台打印:
数据库表:
2、@MappedSuperclass
指定其映射信息应用于从其继承的实体的类。映射的超类没有为其定义单独的表。与MappedSuperclass注释指定的类可以以与实体相同的方式映射,除了映射仅适用于它的子类之外,因为映射超类本身不存在表。当应用于子类时,继承的映射将应用于子类表的上下文中。(说白了,就是将各实体中相同的属性提取到一个添加该注解的父类中,父类不会生成对应的表,但是各子实体类生成的对应表不变。)
这样我们就可以将通用的ID和Auditing相关的属性提取出来。
2.1、id抽象类
- /**
- * 抽象id父类
- *
- * @author caofanqi
- */
- @Getter
- @Setter
- @ToString
- @MappedSuperclass
- public abstract class AbstractID {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- }
2.2、审计功能抽象类
- /**
- * 审计功能抽象类
- * @author caofnqi
- */
- @Getter
- @Setter
- @ToString(callSuper = true)
- @MappedSuperclass
- @EntityListeners(value = AuditingEntityListener.class)
- public abstract class AbstractAuditDomain extends AbstractID {
- @CreatedDate
- private LocalDateTime createdDate;
- @LastModifiedDate
- private LocalDateTime lastModifiedDate;
- @CreatedBy
- @Column(name = "create_by_user_id")
- private Long createdByUserId;
- @LastModifiedBy
- @Column(name = "last_modified_by_user_id")
- private Long lastModifiedUserBy;
- }
2.3、实体类,可以根据是否需要用到选择继承id抽象类,还是审计抽象类
- /**
- * @author caofanqi
- */
- @Getter
- @Setter
- @Entity
- @Builder
- @Table(name = "jpa_audit_person")
- @NoArgsConstructor
- @AllArgsConstructor
- @ToString(callSuper = true)
- public class AuditPerson extends AbstractAuditDomain {
- private String personName;
- }
2.4、修改对应的AuditorAware实现,并指定auditorAwareRef
- /**
- * AuditorAware实现示例,根据自己业务进行实现
- * @author caofanqi
- */
- public class IdAuditorAwareImpl implements AuditorAware<Long> {
- private Optional<AuditUser> currentUser = Optional.empty();
- public void setCurrentUser(AuditUser currentUser){
- this.currentUser = Optional.of(currentUser);
- }
- @Override
- public Optional<Long> getCurrentAuditor() {
- return currentUser.map(AuditUser::getId);
- }
- }
测试类似上面,这里就不贴了。
3、自定义实体监听
Auditing是通过JPA提供的@EntityListeners和@PrePersist、@PreUpdate来完成的。
@EntityListeners,指定要用于实体或映射超类的回调侦听器类。此注释可以应用于实体类或映射的超类。
属性:value,回调侦听器类。
以下注解为相应的生命周期事件指定回调方法。此注释可以应用于实体类、映射超类或回调侦听器类的方法。都是同步机制使用时要注意,可以在使用时,可以在方法中开启异步线程或消息队列。
@PrePersist,新增之前;@PostPersist,新增之后。
@PreUpdate,更新之前;@PostUpdate,更新之后。
@PreRemove,删除之前;@PostRemove,删除之后。
@PostLoad,加载之后。
我们以订单为例:
- /**
- *
- * @author caofanqi
- */
- @Slf4j
- @Getter
- @Setter
- @Entity
- @Builder
- @Table(name = "jpa_order")
- @NoArgsConstructor
- @AllArgsConstructor
- @EntityListeners(value = OrderEntityListener.class)
- public class Order extends AbstractAuditDomain{
- @Column(unique = true)
- private String orderNo;
- @Column(nullable = false)
- private OrderStatus orderStatus;
- @Column(nullable = false)
- private BigDecimal price;
- //其他属性....
- /*
- * 以下方法也可以写在监听类中
- */
- // @PrePersist
- // public void prePersist(){
- // this.setOrderStatus(OrderStatus.NEW);
- // log.info("orderNo: {},status :{},新增之前修改订单状态为NEW",this.getOrderNo(),this.getOrderStatus());
- // }
- //
- // @PostPersist
- // public void postPersist(){
- // log.info("orderNo: {},status :{},新增之后,异步通知仓库进行处理",this.getOrderNo(),this.getOrderStatus());
- // }
- //
- // @PostLoad
- // public void postLoad(){
- // log.info("orderNo: {},status :{},加载之后...",this.getOrderNo(),this.getOrderStatus());
- // }
- //
- // @PreUpdate
- // public void preUpdate(){
- // log.info("orderNo: {},status :{},修改之前.....",this.getOrderNo(),this.getOrderStatus());
- // }
- //
- // @PostUpdate
- // public void postUpdate(){
- // log.info("orderNo: {},status :{},修改之后根据订单状态进行不同的判断",this.getOrderNo(),this.getOrderStatus());
- // }
- //
- // @PreRemove
- // public void preRemove(){
- // log.info("orderNo: {},status :{},删除之前.....",this.getOrderNo(),this.getOrderStatus());
- // }
- //
- //
- // @PostRemove
- // public void postRemove(){
- // log.info("orderNo: {},status :{},删除之后.....",this.getOrderNo(),this.getOrderStatus());
- // }
- }
- /**
- * 订单实体监听类
- * @author caofanqi
- */
- @Slf4j
- public class OrderEntityListener {
- @PrePersist
- public void prePersist(Order order){
- order.setOrderStatus(OrderStatus.NEW);
- log.info("orderNo: {},status :{},新增之前修改订单状态为NEW",order.getOrderNo(),order.getOrderStatus());
- }
- @PostPersist
- public void postPersist(Order order){
- log.info("orderNo: {},status :{},新增之后,异步通知厂库进行处理",order.getOrderNo(),order.getOrderStatus());
- }
- @PostLoad
- public void postLoad(Order order){
- log.info("orderNo: {},status :{},加载之后...",order.getOrderNo(),order.getOrderStatus());
- }
- @PreUpdate
- public void preUpdate(Order order){
- log.info("orderNo: {},status :{},修改之前.....",order.getOrderNo(),order.getOrderStatus());
- }
- @PostUpdate
- public void postUpdate(Order order){
- log.info("orderNo: {},status :{},修改之后根据订单状态进行不同的判断",order.getOrderNo(),order.getOrderStatus());
- }
- @PreRemove
- public void preRemove(Order order){
- log.info("orderNo: {},status :{},删除之前.....",order.getOrderNo(),order.getOrderStatus());
- }
- @PostRemove
- public void postRemove(Order order){
- log.info("orderNo: {},status :{},删除之后.....",order.getOrderNo(),order.getOrderStatus());
- }
- }
测试新增:
测试查询和修改(图中红框中的为jpa save方法更新前自己运行的查询):
测试查询和删除(图中红框中的为jpa delete方法更新前自己运行的查询):
- 源码地址:https://github.com/caofanqi/study-spring-data-jpa
学习Spring-Data-Jpa(十五)---Auditing与@MappedSuperclass的更多相关文章
- 学习Spring Data JPA
简介 Spring Data 是spring的一个子项目,在官网上是这样解释的: Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特殊 ...
- 学习-spring data jpa
spring data jpa对照表 Keyword Sample JPQL snippet And findByLastnameAndFirstname - where x.lastname = ? ...
- 【Spring Data 系列学习】了解 Spring Data JPA 、 Jpa 和 Hibernate
在开始学习 Spring Data JPA 之前,首先讨论下 Spring Data Jpa.JPA 和 Hibernate 之前的关系. JPA JPA 是 Java Persistence API ...
- Spring Data Jpa 入门学习
本文主要讲解 springData Jpa 入门相关知识, 了解JPA规范与Jpa的实现,搭建springboot+dpringdata jpa环境实现基础增删改操作,适合新手学习,老鸟绕道~ 1. ...
- spring boot(五):spring data jpa的使用
在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jpa 常见用法以及注意事项 使用spr ...
- 一步步学习 Spring Data 系列之JPA(二)
继上一篇文章对Spring Data JPA更深( )一步剖析. 上一篇只是简单的介绍了Spring Data JPA的简单使用,而往往在项目中这一点功能并不能满足我们的需求.这是当然的,在业务中查询 ...
- Spring Data JPA 学习记录1 -- 单向1:N关联的一些问题
开新坑 开新坑了(笑)....公司项目使用的是Spring Data JPA做持久化框架....学习了一段时间以后发现了一点值得注意的小问题.....与大家分享 主要是针对1:N单向关联产生的一系列问 ...
- spring data jpa入门学习
本文主要介绍下spring data jpa,主要聊聊为何要使用它进行开发以及它的基本使用.本文主要是入门介绍,并在最后会留下完整的demo供读者进行下载,从而了解并且开始使用spring data ...
- 展开被 SpringBoot 玩的日子 《 五 》 spring data jpa 的使用
在上篇文章< 展开被 SpringBoot 玩的日子 < 二 >WEB >中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring da ...
随机推荐
- springboot异步线程(二)
前言 本篇文章针对上篇文章springboot异步线程,有一位大佬在评论中提出第一点是错误的,当时看到了这个问题,最近刚好有空,针对第一点的问题去搜索了不少的文章: 问题 我在文章中第一点去验证:Sc ...
- The four Day 给出一个平衡字符串,将它分割成尽可能多的平衡字符串
""" 在一个「平衡字符串」中,'L' 和 'R' 字符的数量是相同的. 给出一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串. 返回可以通过分割得到的平衡字符串的 ...
- yii框架无限极分类的做法
用yii框架做了一个无限极分类,主要的数组转换都是粘贴的别人的代码,但还是不要脸的写出来,方便以后自己看 用的是递归,不是path路径 控制器: protected function subtree( ...
- mongoDB 分组并对分组结果筛选类似于SQL中的(group by xxx having ) 附带Java代码
今天需要做一个筛选程序,因为数据放在mongodb中,没写过分组的查询语句,查了一些资料,终于写出来了,分享给各位小伙伴 需求是 查询 学员 在2019-07-29之后未同步的数据(同一个学员需要2条 ...
- C#采集摄像头实时画面和抓拍
在.net中,并没有简单直接的操纵摄像头的类.那么如何简单快捷地采集摄像头的画面,进行抓拍等操作呢?答案是调用SharpCapture!专业采集摄像头画面等数据的类库.下面开始演示关键代码,您也可以在 ...
- Java知识回顾 (16)常用操作的Java示例
环境设置 Java 实例 – 如何编译一个Java 文件? Java 实例 – Java 如何运行一个编译过的类文件? Java 实例 - 如何执行指定class文件目录(classpath)? Ja ...
- 如何解决NoSuchMethodError
背景 工作中写单测,本来用的Mockito,但是为了mock方法里调用的其他静态方法,所以需要使用powermock,于是开始报错. 我把包引入了,然后照着网上的写单测代码,写完了之后运行.噩梦开始. ...
- 打开文件报“EFailed to load resource: net::ERR_FILE_NOT_FOUND”错误
类似这样: 引入文件的路径错误
- Appscan漏洞 之 加密会话(SSL)Cookie 中缺少 Secure 属性
近期 Appscan扫描出漏洞 加密会话(SSL)Cookie 中缺少 Secure 属性,已做修复,现进行总结如下: 1.1.攻击原理 任何以明文形式发送到服务器的 cookie.会话令牌或用户凭证 ...
- mysql DDL数据定义语言
DDL数据定义语言 本节涉及MySQL关键字:create.alter(rename,add,chang,modify,drop).drop.delete.truncate等. -- 创建表:-- 数 ...