日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Data JPA 从入门到精通~Auditing及其事件详解

發(fā)布時間:2024/7/23 javascript 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Data JPA 从入门到精通~Auditing及其事件详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?Auditing 及其事件詳解

Auditing 翻譯過來是審計和審核,Spring 的優(yōu)秀之處在于幫我們想到了很多繁瑣事情的解決方案,我們在實際的業(yè)務系統(tǒng)中,針對一張表的操作大部分是需要記錄誰什么時間創(chuàng)建的,誰什么時間修改的,并且能讓我們方便的記錄操作日志。Spring Data JPA 為我們提供了審計功能的架構實現(xiàn),提供了四個注解專門解決這件事情:

  • @CreatedBy 哪個用戶創(chuàng)建的。
  • @CreatedDate 創(chuàng)建的時間。
  • @LastModifiedBy 修改實體的用戶。
  • @LastModifiedDate 最后一次修改時間。

Auditing 如何配置

我們以一個快速的例子,看看它是怎么配置生效的。

(1)先新建一個 @Entity:UserCustomerEntity 里面的寫法如下。

@Entity @Table(name = "user_customer", schema = "test", catalog = "") @EntityListeners(AuditingEntityListener.class) public class UserCustomerEntity {@Id@Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;@CreatedDate@Column(name = "create_time", nullable = true)private Date createTime;@CreatedBy@Column(name = "create_user_id", nullable = true)private Integer createUserId;@LastModifiedBy@Column(name = "last_modified_user_id", nullable = true)private Integer lastModifiedUserId;@LastModifiedDate@Column(name = "last_modified_time", nullable = true)private Date lastModifiedTime;@Column(name = "customer_name", nullable = true, length = 50)private String customerName;@Column(name = "customer_email", nullable = true, length = 50)private String customerEmail; ...... }

@Entity 實體中我們需要做兩點:

  • 相應的字段添加 @CreatedBy、@CreatedDate、@LastModifiedBy and @LastModifiedDate注解。
  • 增加 @EntityListeners(AuditingEntityListener.class)。

(2)實現(xiàn) AuditorAware 接口告訴 JPA 當前的用戶是誰。

實現(xiàn) AuditorAware 接口,實現(xiàn) getCurrentAuditor 方法,返回一個 Integer 的 user ID。以下代碼介紹了兩種做法:

public class MyAuditorAware implements AuditorAware<Integer> {/*** Returns the current auditor of the application.* @return the current auditor*/@Overridepublic Integer getCurrentAuditor() { // 第一種方式:如果我們集成了spring的Security,我們直接通過如下方法即可獲得當前請求的用戶ID. // Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // if (authentication == null || !authentication.isAuthenticated()) { // return null; // } // return ((LoginUserInfo) authentication.getPrincipal()).getUser().getId();//第二種方式通過request里面取或者session里面取ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();return (Integer) servletRequestAttributes.getRequest().getSession().getAttribute("userId");} }

而 AuditorAware 的源碼如下:

public interface AuditorAware<T> {T getCurrentAuditor(); }

通過實現(xiàn) AuditorAware 接口的 getCurrentAuditor() 方法告訴 JPA 當前的用戶是誰,里面實現(xiàn)方法千差萬別,作者舉例了兩種最常見的:

  • 通過 Security 取。
  • 通過 Request 取。

(3)通過 @EnableJpaAuditing 注解開啟 JPA 的 Auditing 功能。

并且告訴應用 AuditorAware 的實現(xiàn)類是誰,也就是我們通過 @Bean 注解把上面的實現(xiàn)類放到 Spring 的 Bean 管理里面,當然了也可以上面的類加上 @Component。具體配置方式如下:

@SpringBootApplication @EnableJpaAuditing public class QuickStartApplication {public static void main(String[] args) {SpringApplication.run(QuickStartApplication.class, args);}@Beanpublic AuditorAware<Integer> auditorProvider() {return new MyAuditorAwareImpl();} }

驗證結果如下。

通過以上的三步,我們已經(jīng)完成了 auting 的配置,通過 userCustomerRepository.save(new UserCustomerEntity("1","Jack")); 的執(zhí)行,我們看數(shù)據(jù)庫里面的 4 個字段已經(jīng)給填上去了。

@MappedSuperclass

實際工作中我們還會對上面的實體部分進行改進,引入 @MappedSuperclass 注解,我們將 @Id、@CreatedBy、@CreatedDate、@LastModifiedBy and @LastModifiedDate 抽象到一個公用的基類里面,方便公用和形成每個表的字段約束。可以將其放到我們公司的框架代碼上,對表設計形成統(tǒng)一的強約束。

步驟如下:

(1)改進后我們新增一個 AbstractAuditable 的抽象類:

@MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class AbstractAuditable {@Id@Column(name = "id", nullable = false)@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;@CreatedDate@Column(name = "create_time", nullable = true)private Date createTime;@CreatedBy@Column(name = "create_user_id", nullable = true)private Integer createUserId;@LastModifiedBy@Column(name = "last_modified_user_id", nullable = true)private Integer lastModifiedUserId;@LastModifiedDate@Column(name = "last_modified_time", nullable = true)private Date lastModifiedTime; ...... }

(2)而我們每個需要 Auditing 的實體只需要繼承 AbstractAuditable 即可。

內(nèi)容如下:

@Entity @Table(name = "user_customer", schema = "test", catalog = "") public class UserCustomerEntity extends AbstractAuditable {@Column(name = "customer_name", nullable = true, length = 50)private String customerName;@Column(name = "customer_email", nullable = true, length = 50)private String customerEmail; ......}

Auditing 原理解析

(1)我們先看一下關鍵的幾個源碼的關系圖:

(2)AuditingEntityListener 的源碼如下:

@Configurable public class AuditingEntityListener {private ObjectFactory<AuditingHandler> handler;public void setAuditingHandler(ObjectFactory<AuditingHandler> auditingHandler) {Assert.notNull(auditingHandler, "AuditingHandler must not be null!");this.handler = auditingHandler;}//在新增之前通過handler來往我們的@Entity里面的auditor的那些字段塞值。@PrePersistpublic void touchForCreate(Object target) {if (handler != null) {handler.getObject().markCreated(target);}}//在更新之前通過handler來往我們的@Entity里面的auditor的那些字段塞值。@PreUpdatepublic void touchForUpdate(Object target) {if (handler != null) {handler.getObject().markModified(target);}} }

(3)通過調(diào)用關系圖和 AuditingEntityListener,我們其實可以發(fā)現(xiàn)以下兩點情況:

  • AuditingEntityListener 通過委托設計模式,委托 AuditingHandler 進行處理,而我們看 AuditingHandler 的源碼會發(fā)現(xiàn),里面就是根據(jù) ID 和 Version(后面介紹)來判斷我們的對象是新增還是更新,從而來更改時間字段和 User 字段。而 User 字段是通過 AuditorAware 的實現(xiàn)類來取的,并且 AuditorAware 沒有默認實現(xiàn)類,只有我們自己的實現(xiàn)類,也就是 AuditorAware 的實現(xiàn)類必須我們自己來定義,否則啟動會報錯。
  • AuditingEntityListener 的代碼如此簡單,我們能不能自定義呢?答案是肯定的,通過 @PrePersist、@PreUpdate 查看源碼得出,Java Persistence API 底層又幫我們提供的 Callbacks,而這些回調(diào)方法,用于偵聽保存、查詢(抓取)、更新和刪除數(shù)據(jù)庫中的數(shù)據(jù)。注解方式如下:

Type

描述

@PrePersist

新增之前

@PreRemove

刪除之前

@PostPersist

新增之后

@PostRemove

刪除之后

@PreUpdate

更新之前

@PostUpdate

更新之后

@PostLoad

加載后

注意:這個方法都是同步機制,一但報錯將會影響所有底層代碼執(zhí)行。在實際工作中實現(xiàn)這些方法的時候,方法體里面開啟異步線程,或者消息隊列,來異步處理日志,或者更繁重的工作。

Listener 事件的擴展

自定義 EntityListener

隨著 DDD 的設計模式逐漸被大家認可和熱捧,JPA 通過這種 Listener 這種機制可以很好的實現(xiàn)事件分離、狀體分離。假如,訂單的狀態(tài)變化可能對我們來說比較重要,我們需要定一個類去監(jiān)聽訂單狀態(tài)變更,通知相應的邏輯代碼各自去干各自的活。

(1)新增一個 OrderStatusAuditListener 類,在相應的操作上添加 Callbacks 注解。

public class OrderStatusAuditListener {@PostPersistprivate void postPersist(OrderEntiy entity) {//當更新的時候做一些邏輯判斷,及其事件通知。}@PostRemoveprivate void PostRemove(OrderEntiy entity) {//當刪除的時候做一些邏輯判斷。}@PostUpdate private void PostUpdate(OrderEntiy entity) {//當更新的時候// entity.getOrderStatus(),做一些邏輯判斷} }

(2)我們的訂單實體變化如下:

@Entity @Table("orders") @EntityListeners({AuditingEntityListener.class, OrderStatusAuditListener.class}) public class OrderEntity extends AbstractAuditable{@Enumerated(EnumType.STRING)@Column("order_status")private OrderStatusEnum orderStatus;...... }

即可完成自定義 EntityListener。

實際工作記錄操作日志的實例

public class ActionsLogsAuditListener {private static final Logger logger = LoggerFactory.getLogger(ActionsLogsAuditListener.class);@PostLoadprivate void postLoad(Object entity) {this.notice(entity, OperateType.load);}@PostPersistprivate void postPersist(Object entity) {this.notice(entity, OperateType.create);}@PostRemoveprivate void PostRemove(Object entity) {this.notice(entity, OperateType.remove);}@PostUpdateprivate void PostUpdate(Object entity) {this.notice(entity, OperateType.update);}private void notice(Object entity, OperateType type) {logger.info("{} 執(zhí)行了 {} 操作", entity, type.getDescription());//我們通過active mq 異步發(fā)出消息處理事件ActiveMqEventManager.notice(new ActiveMqEvent(type, entity));}enum OperateType {create("創(chuàng)建"), remove("刪除"),update("修改"),load("查詢");private final String description;OperateType(String description) {this.description=description;}public String getDescription() {return description;}} }

我們通過自定義的 ActionsLogsAuditListener 來監(jiān)聽我們要處理日志的實體,然后將事件變更,通過消息隊列進行異步處理,這樣就可以完全解耦了。當然了,這里我們解耦的方式也可以通過 Spring 的事件機制進行解決。通過工作中的此示例,來幫助大家更好的理解 Audit 的機制,順便說一下處理操作的日志的正確思路,記錄當前真實發(fā)生的數(shù)據(jù)和狀態(tài),及其時間即可,具體變化了什么那是在業(yè)務展示層面上要做的事情,這里沒有必要做比對的事情,記住這一點之后就會讓你的日志處理實現(xiàn)機制豁然明朗,變得容易許多。

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結

以上是生活随笔為你收集整理的Spring Data JPA 从入门到精通~Auditing及其事件详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。