學習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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM