1、Auditing

  一般我们针对一张表的操作需要记录下来,是谁修改的,修改时间是什么,Spring-Data为我们提供了支持。

  1.1、在实体类中使用Spring-Data为我们提供的四个注解(也可以选择实现Auditable接口或继承AbstractAuditable类,推荐使用注解)

  1.2、在实体上添加@EntityListeners(value = AuditingEntityListener.class)启动对当前实体的监听。

  1. /**
  2. * 测试spring-data为我们提供的审计功能
  3. *
  4. * @author caofanqi
  5. */
  6. @Data
  7. @Entity
  8. @Builder
  9. @Table(name = "jpa_audit_user")
  10. @NoArgsConstructor
  11. @AllArgsConstructor
  12. @EntityListeners(value = AuditingEntityListener.class)
  13. public class AuditUser {
  14.  
  15. @Id
  16. @GeneratedValue(strategy = GenerationType.IDENTITY)
  17. private Long id;
  18.  
  19. @Column(unique = true)
  20. private String name;
  21.  
  22. @CreatedDate
  23. private LocalDateTime createdDate;
  24.  
  25. @LastModifiedDate
  26. private LocalDateTime lastModifiedDate;
  27.  
  28. @CreatedBy
  29. @ManyToOne
  30. private AuditUser createdBy;
  31.  
  32. @LastModifiedBy
  33. @ManyToOne
  34. private AuditUser lastModifiedBy;
  35. }

  1.3、如果在实体中使用了@CreatedBy或者@LastModifiedBy需要实现AuditorAware<T>接口,告诉Spring-Data当前审计用户是谁。(一般项目中从spring security或token中获取)

  1. /**
  2. * 获取当前的审计人,实际项目中可以从Spring Security中或Token/{session}中获取,这里只是举个例子进行模拟。
  3. * @author caofanqi
  4. */
  5. public class AuditorAwareImpl implements AuditorAware<AuditUser> {
  6.  
  7. private Optional<AuditUser> currentUser = Optional.empty();
  8.  
  9. public void setCurrentUser(AuditUser currentUser){
  10. this.currentUser = Optional.of(currentUser);
  11. }
  12.  
  13. @Override
  14. public Optional<AuditUser> getCurrentAuditor() {
  15. //要使用的当前用户
  16. return currentUser;
  17. }
  18.  
  19. }

  1.4、在启动类上添加@EnableJpaAuditing启动审计功能。

  1.5、如果ApplicationContext中只有一个AuditorAware类型的bean,Spring-Date会自动选择,如果又多个,需要通过@EnableJpaAuditing注解的auditorAwareRef属性进行设置。

  1. /**
  2. * 启动类
  3. * @author caofanqi
  4. */
  5. @SpringBootApplication
  6. @EnableAsync
  7. @EnableJpaRepositories(
  8. /*queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND*/
  9. /* ,repositoryImplementationPostfix = "MyPostfix",*/
  10. /*repositoryBaseClass = MyRepositoryImpl.class*/)
  11. @EnableJpaAuditing
  12. public class StudySpringDataJpaApplication {
  13.  
  14. public static void main(String[] args) {
  15. SpringApplication.run(StudySpringDataJpaApplication.class, args);
  16. }
  17.  
  18. /**
  19. * 如果ApplicationContext中只有一个AuditorAware类型的bean,Spring-Date会自动选择,
  20. * 如果又多个,需要通过@EnableJpaAuditing注解的auditorAwareRef属性进行设置。
  21. */
  22. @Bean
  23. public AuditorAware<AuditUser> auditorProvider() {
  24. return new AuditorAwareImpl();
  25. }
  26.  
  27. }

  1.6、测试用例,及生成的表

  1. @SpringBootTest
  2. class AuditUserRepositoryTest {
  3.  
  4. @Resource
  5. private AuditUserRepository auditUserRepository;
  6.  
  7. @Resource
  8. private AuditorAwareImpl auditorAware;
  9.  
  10. @Test
  11. void testAuditDate(){
  12. /*
  13. *不设置创建和修改时间,由springl-data替我们完成
  14. */
  15. AuditUser audit = AuditUser.builder().name("张三").build();
  16. AuditUser save = auditUserRepository.save(audit);
  17. System.out.println(save);
  18. }
  19.  
  20. @Test
  21. void testAuditUser(){
  22.  
  23. /*
  24. * 模拟当前用户
  25. */
  26. auditorAware.setCurrentUser(auditUserRepository.findByName("张三"));
  27.  
  28. /*
  29. * 这里不设置是谁保存的,看spring-data是否会为我们完成
  30. */
  31. AuditUser audit = AuditUser.builder().name("李四").build();
  32.  
  33. AuditUser save = auditUserRepository.save(audit);
  34. System.out.println(save);
  35. }
  36.  
  37. }

  testAuditDate控制台打印:

  

  testAuditUser控制台打印:

  

  数据库表:

  

2、@MappedSuperclass

  指定其映射信息应用于从其继承的实体的类。映射的超类没有为其定义单独的表。与MappedSuperclass注释指定的类可以以与实体相同的方式映射,除了映射仅适用于它的子类之外,因为映射超类本身不存在表。当应用于子类时,继承的映射将应用于子类表的上下文中。(说白了,就是将各实体中相同的属性提取到一个添加该注解的父类中,父类不会生成对应的表,但是各子实体类生成的对应表不变。)

  这样我们就可以将通用的ID和Auditing相关的属性提取出来。

  2.1、id抽象类

  1. /**
  2. * 抽象id父类
  3. *
  4. * @author caofanqi
  5. */
  6. @Getter
  7. @Setter
  8. @ToString
  9. @MappedSuperclass
  10. public abstract class AbstractID {
  11.  
  12. @Id
  13. @GeneratedValue(strategy = GenerationType.IDENTITY)
  14. private Long id;
  15.  
  16. }

  2.2、审计功能抽象类

  1. /**
  2. * 审计功能抽象类
  3. * @author caofnqi
  4. */
  5. @Getter
  6. @Setter
  7. @ToString(callSuper = true)
  8. @MappedSuperclass
  9. @EntityListeners(value = AuditingEntityListener.class)
  10. public abstract class AbstractAuditDomain extends AbstractID {
  11.  
  12. @CreatedDate
  13. private LocalDateTime createdDate;
  14.  
  15. @LastModifiedDate
  16. private LocalDateTime lastModifiedDate;
  17.  
  18. @CreatedBy
  19. @Column(name = "create_by_user_id")
  20. private Long createdByUserId;
  21.  
  22. @LastModifiedBy
  23. @Column(name = "last_modified_by_user_id")
  24. private Long lastModifiedUserBy;
  25.  
  26. }

  2.3、实体类,可以根据是否需要用到选择继承id抽象类,还是审计抽象类

  1. /**
  2. * @author caofanqi
  3. */
  4. @Getter
  5. @Setter
  6. @Entity
  7. @Builder
  8. @Table(name = "jpa_audit_person")
  9. @NoArgsConstructor
  10. @AllArgsConstructor
  11. @ToString(callSuper = true)
  12. public class AuditPerson extends AbstractAuditDomain {
  13.  
  14. private String personName;
  15.  
  16. }

  2.4、修改对应的AuditorAware实现,并指定auditorAwareRef

  1. /**
  2. * AuditorAware实现示例,根据自己业务进行实现
  3. * @author caofanqi
  4. */
  5. public class IdAuditorAwareImpl implements AuditorAware<Long> {
  6.  
  7. private Optional<AuditUser> currentUser = Optional.empty();
  8.  
  9. public void setCurrentUser(AuditUser currentUser){
  10. this.currentUser = Optional.of(currentUser);
  11. }
  12.  
  13. @Override
  14. public Optional<Long> getCurrentAuditor() {
  15. return currentUser.map(AuditUser::getId);
  16. }
  17.  
  18. }

  

  

  测试类似上面,这里就不贴了。

3、自定义实体监听

  Auditing是通过JPA提供的@EntityListeners和@PrePersist、@PreUpdate来完成的。

  @EntityListeners,指定要用于实体或映射超类的回调侦听器类。此注释可以应用于实体类或映射的超类。
    属性:value,回调侦听器类。
  以下注解为相应的生命周期事件指定回调方法。此注释可以应用于实体类、映射超类或回调侦听器类的方法。都是同步机制使用时要注意,可以在使用时,可以在方法中开启异步线程或消息队列。
  @PrePersist,新增之前;@PostPersist,新增之后。
  @PreUpdate,更新之前;@PostUpdate,更新之后。
  @PreRemove,删除之前;@PostRemove,删除之后。
  @PostLoad,加载之后。

  我们以订单为例:

  1. /**
  2. *
  3. * @author caofanqi
  4. */
  5. @Slf4j
  6. @Getter
  7. @Setter
  8. @Entity
  9. @Builder
  10. @Table(name = "jpa_order")
  11. @NoArgsConstructor
  12. @AllArgsConstructor
  13. @EntityListeners(value = OrderEntityListener.class)
  14. public class Order extends AbstractAuditDomain{
  15.  
  16. @Column(unique = true)
  17. private String orderNo;
  18.  
  19. @Column(nullable = false)
  20. private OrderStatus orderStatus;
  21.  
  22. @Column(nullable = false)
  23. private BigDecimal price;
  24.  
  25. //其他属性....
  26.  
  27. /*
  28. * 以下方法也可以写在监听类中
  29. */
  30.  
  31. // @PrePersist
  32. // public void prePersist(){
  33. // this.setOrderStatus(OrderStatus.NEW);
  34. // log.info("orderNo: {},status :{},新增之前修改订单状态为NEW",this.getOrderNo(),this.getOrderStatus());
  35. // }
  36. //
  37. // @PostPersist
  38. // public void postPersist(){
  39. // log.info("orderNo: {},status :{},新增之后,异步通知仓库进行处理",this.getOrderNo(),this.getOrderStatus());
  40. // }
  41. //
  42. // @PostLoad
  43. // public void postLoad(){
  44. // log.info("orderNo: {},status :{},加载之后...",this.getOrderNo(),this.getOrderStatus());
  45. // }
  46. //
  47. // @PreUpdate
  48. // public void preUpdate(){
  49. // log.info("orderNo: {},status :{},修改之前.....",this.getOrderNo(),this.getOrderStatus());
  50. // }
  51. //
  52. // @PostUpdate
  53. // public void postUpdate(){
  54. // log.info("orderNo: {},status :{},修改之后根据订单状态进行不同的判断",this.getOrderNo(),this.getOrderStatus());
  55. // }
  56. //
  57. // @PreRemove
  58. // public void preRemove(){
  59. // log.info("orderNo: {},status :{},删除之前.....",this.getOrderNo(),this.getOrderStatus());
  60. // }
  61. //
  62. //
  63. // @PostRemove
  64. // public void postRemove(){
  65. // log.info("orderNo: {},status :{},删除之后.....",this.getOrderNo(),this.getOrderStatus());
  66. // }
  67.  
  68. }
  1. /**
  2. * 订单实体监听类
  3. * @author caofanqi
  4. */
  5. @Slf4j
  6. public class OrderEntityListener {
  7.  
  8. @PrePersist
  9. public void prePersist(Order order){
  10. order.setOrderStatus(OrderStatus.NEW);
  11. log.info("orderNo: {},status :{},新增之前修改订单状态为NEW",order.getOrderNo(),order.getOrderStatus());
  12. }
  13.  
  14. @PostPersist
  15. public void postPersist(Order order){
  16. log.info("orderNo: {},status :{},新增之后,异步通知厂库进行处理",order.getOrderNo(),order.getOrderStatus());
  17. }
  18.  
  19. @PostLoad
  20. public void postLoad(Order order){
  21. log.info("orderNo: {},status :{},加载之后...",order.getOrderNo(),order.getOrderStatus());
  22. }
  23.  
  24. @PreUpdate
  25. public void preUpdate(Order order){
  26. log.info("orderNo: {},status :{},修改之前.....",order.getOrderNo(),order.getOrderStatus());
  27. }
  28.  
  29. @PostUpdate
  30. public void postUpdate(Order order){
  31. log.info("orderNo: {},status :{},修改之后根据订单状态进行不同的判断",order.getOrderNo(),order.getOrderStatus());
  32. }
  33.  
  34. @PreRemove
  35. public void preRemove(Order order){
  36. log.info("orderNo: {},status :{},删除之前.....",order.getOrderNo(),order.getOrderStatus());
  37. }
  38.  
  39. @PostRemove
  40. public void postRemove(Order order){
  41. log.info("orderNo: {},status :{},删除之后.....",order.getOrderNo(),order.getOrderStatus());
  42. }
  43.  
  44. }

  测试新增:

  

  测试查询和修改(图中红框中的为jpa save方法更新前自己运行的查询):

  

  测试查询和删除(图中红框中的为jpa delete方法更新前自己运行的查询):

   

  1. 源码地址:https://github.com/caofanqi/study-spring-data-jpa

学习Spring-Data-Jpa(十五)---Auditing与@MappedSuperclass的更多相关文章

  1. 学习Spring Data JPA

    简介 Spring Data 是spring的一个子项目,在官网上是这样解释的: Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特​​殊 ...

  2. 学习-spring data jpa

    spring data jpa对照表 Keyword Sample JPQL snippet And findByLastnameAndFirstname - where x.lastname = ? ...

  3. 【Spring Data 系列学习】了解 Spring Data JPA 、 Jpa 和 Hibernate

    在开始学习 Spring Data JPA 之前,首先讨论下 Spring Data Jpa.JPA 和 Hibernate 之前的关系. JPA JPA 是 Java Persistence API ...

  4. Spring Data Jpa 入门学习

    本文主要讲解 springData Jpa 入门相关知识, 了解JPA规范与Jpa的实现,搭建springboot+dpringdata jpa环境实现基础增删改操作,适合新手学习,老鸟绕道~ 1. ...

  5. spring boot(五):spring data jpa的使用

    在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jpa 常见用法以及注意事项 使用spr ...

  6. 一步步学习 Spring Data 系列之JPA(二)

    继上一篇文章对Spring Data JPA更深( )一步剖析. 上一篇只是简单的介绍了Spring Data JPA的简单使用,而往往在项目中这一点功能并不能满足我们的需求.这是当然的,在业务中查询 ...

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

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

  8. spring data jpa入门学习

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

  9. 展开被 SpringBoot 玩的日子 《 五 》 spring data jpa 的使用

    在上篇文章< 展开被 SpringBoot 玩的日子 < 二 >WEB >中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring da ...

随机推荐

  1. springboot异步线程(二)

    前言 本篇文章针对上篇文章springboot异步线程,有一位大佬在评论中提出第一点是错误的,当时看到了这个问题,最近刚好有空,针对第一点的问题去搜索了不少的文章: 问题 我在文章中第一点去验证:Sc ...

  2. The four Day 给出一个平衡字符串,将它分割成尽可能多的平衡字符串

    """ 在一个「平衡字符串」中,'L' 和 'R' 字符的数量是相同的. 给出一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串. 返回可以通过分割得到的平衡字符串的 ...

  3. yii框架无限极分类的做法

    用yii框架做了一个无限极分类,主要的数组转换都是粘贴的别人的代码,但还是不要脸的写出来,方便以后自己看 用的是递归,不是path路径 控制器: protected function subtree( ...

  4. mongoDB 分组并对分组结果筛选类似于SQL中的(group by xxx having ) 附带Java代码

    今天需要做一个筛选程序,因为数据放在mongodb中,没写过分组的查询语句,查了一些资料,终于写出来了,分享给各位小伙伴 需求是 查询 学员 在2019-07-29之后未同步的数据(同一个学员需要2条 ...

  5. C#采集摄像头实时画面和抓拍

    在.net中,并没有简单直接的操纵摄像头的类.那么如何简单快捷地采集摄像头的画面,进行抓拍等操作呢?答案是调用SharpCapture!专业采集摄像头画面等数据的类库.下面开始演示关键代码,您也可以在 ...

  6. Java知识回顾 (16)常用操作的Java示例

    环境设置 Java 实例 – 如何编译一个Java 文件? Java 实例 – Java 如何运行一个编译过的类文件? Java 实例 - 如何执行指定class文件目录(classpath)? Ja ...

  7. 如何解决NoSuchMethodError

    背景 工作中写单测,本来用的Mockito,但是为了mock方法里调用的其他静态方法,所以需要使用powermock,于是开始报错. 我把包引入了,然后照着网上的写单测代码,写完了之后运行.噩梦开始. ...

  8. 打开文件报“EFailed to load resource: net::ERR_FILE_NOT_FOUND”错误

    类似这样: 引入文件的路径错误

  9. Appscan漏洞 之 加密会话(SSL)Cookie 中缺少 Secure 属性

    近期 Appscan扫描出漏洞 加密会话(SSL)Cookie 中缺少 Secure 属性,已做修复,现进行总结如下: 1.1.攻击原理 任何以明文形式发送到服务器的 cookie.会话令牌或用户凭证 ...

  10. mysql DDL数据定义语言

    DDL数据定义语言 本节涉及MySQL关键字:create.alter(rename,add,chang,modify,drop).drop.delete.truncate等. -- 创建表:-- 数 ...