zoukankan      html  css  js  c++  java
  • 学习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
  • 相关阅读:
    IntelliJ IDEA教程之如何clean或者install Maven项目
    mysql 导出表,导出数据 命令
    import require
    https确实加密了。 抓包是一个中间人攻击过程
    密码学部分算法
    账号密码加密的方案
    查看git提交细节
    使用源安装java JDK
    updated stream stash changes
    Hibernate与Jpa的关系
  • 原文地址:https://www.cnblogs.com/caofanqi/p/11996718.html
Copyright © 2011-2022 走看看