SpringData JPA審計(jì)功能(@CreatedDate與@LastModifiedDate)實(shí)現(xiàn)
引言
在企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中,數(shù)據(jù)審計(jì)是一項(xiàng)至關(guān)重要的功能。所謂數(shù)據(jù)審計(jì),是指對(duì)數(shù)據(jù)的創(chuàng)建、修改等操作進(jìn)行跟蹤記錄,以便于后續(xù)的數(shù)據(jù)分析、問(wèn)題追蹤和安全審核。Spring Data JPA提供了強(qiáng)大的審計(jì)功能,通過(guò)簡(jiǎn)單的注解配置,即可實(shí)現(xiàn)對(duì)實(shí)體創(chuàng)建時(shí)間、最后修改時(shí)間、創(chuàng)建人和修改人的自動(dòng)記錄。本文將深入探討Spring Data JPA的審計(jì)功能,重點(diǎn)介紹@CreatedDate與@LastModifiedDate注解的實(shí)現(xiàn)原理及使用方法,幫助開(kāi)發(fā)者構(gòu)建健壯的數(shù)據(jù)審計(jì)系統(tǒng)。
一、Spring Data JPA審計(jì)功能概述
Spring Data JPA的審計(jì)功能是通過(guò)實(shí)體生命周期事件和AOP切面實(shí)現(xiàn)的。它可以在實(shí)體被持久化和更新時(shí),自動(dòng)填充審計(jì)字段,從而避免了手動(dòng)設(shè)置這些值的繁瑣工作。
1.1 核心審計(jì)注解
Spring Data JPA提供了四個(gè)核心的審計(jì)注解:
@CreatedDate
:標(biāo)記實(shí)體創(chuàng)建時(shí)間字段@LastModifiedDate
:標(biāo)記實(shí)體最后修改時(shí)間字段@CreatedBy
:標(biāo)記實(shí)體創(chuàng)建者字段@LastModifiedBy
:標(biāo)記實(shí)體最后修改者字段
這些注解都位于org.springframework.data.annotation
包中,是Spring Data通用的審計(jì)注解,不僅限于JPA使用。
1.2 審計(jì)功能的工作原理
Spring Data JPA的審計(jì)功能主要通過(guò)以下幾個(gè)組件協(xié)同工作:
AuditingEntityListener
:JPA實(shí)體監(jiān)聽(tīng)器,負(fù)責(zé)捕獲實(shí)體的生命周期事件AuditingHandler
:處理審計(jì)信息的填充邏輯DateTimeProvider
:提供當(dāng)前時(shí)間的接口AuditorAware
:提供當(dāng)前用戶(hù)信息的接口
當(dāng)啟用審計(jì)功能后,每當(dāng)實(shí)體被創(chuàng)建或更新,AuditingEntityListener
會(huì)捕獲相應(yīng)的事件,并調(diào)用AuditingHandler
對(duì)標(biāo)記了審計(jì)注解的字段進(jìn)行填充。
二、基礎(chǔ)配置
要使用Spring Data JPA的審計(jì)功能,首先需要進(jìn)行必要的配置。
2.1 啟用JPA審計(jì)功能
在Spring Boot應(yīng)用中,通過(guò)@EnableJpaAuditing
注解啟用JPA審計(jì)功能:
package com.example.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; /** * JPA審計(jì)功能配置類(lèi) */ @Configuration @EnableJpaAuditing // 啟用JPA審計(jì)功能 public class JpaAuditingConfig { // 可以在這里配置審計(jì)相關(guān)的Bean }
2.2 創(chuàng)建審計(jì)基類(lèi)
通常,我們會(huì)創(chuàng)建一個(gè)包含審計(jì)字段的基類(lèi),讓需要審計(jì)的實(shí)體繼承這個(gè)基類(lèi):
package com.example.entity; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; /** * 包含審計(jì)字段的基礎(chǔ)實(shí)體類(lèi) */ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) // 注冊(cè)實(shí)體監(jiān)聽(tīng)器 public abstract class AuditableEntity { @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; // Getter和Setter方法 public LocalDateTime getCreatedDate() { return createdDate; } public void setCreatedDate(LocalDateTime createdDate) { this.createdDate = createdDate; } public LocalDateTime getLastModifiedDate() { return lastModifiedDate; } public void setLastModifiedDate(LocalDateTime lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; } }
在上面的代碼中:
@MappedSuperclass
注解表明這是一個(gè)映射超類(lèi),其字段將被映射到子類(lèi)的表中@EntityListeners(AuditingEntityListener.class)
注冊(cè)了JPA實(shí)體監(jiān)聽(tīng)器,用于捕獲實(shí)體的生命周期事件@CreatedDate
標(biāo)記了實(shí)體創(chuàng)建時(shí)間字段@LastModifiedDate
標(biāo)記了實(shí)體最后修改時(shí)間字段updatable = false
確保createdDate
字段在更新時(shí)不會(huì)被修改
三、實(shí)現(xiàn)時(shí)間審計(jì)
時(shí)間審計(jì)是最基本的審計(jì)功能,涉及到@CreatedDate
和@LastModifiedDate
注解的使用。
3.1 使用審計(jì)基類(lèi)
創(chuàng)建業(yè)務(wù)實(shí)體類(lèi)并繼承審計(jì)基類(lèi):
package com.example.entity; import javax.persistence.*; import java.math.BigDecimal; /** * 產(chǎn)品實(shí)體類(lèi) */ @Entity @Table(name = "tb_product") public class Product extends AuditableEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name", nullable = false) private String name; @Column(name = "description") private String description; @Column(name = "price", precision = 10, scale = 2) private BigDecimal price; @Column(name = "stock") private Integer stock; // Getter和Setter方法省略 }
繼承AuditableEntity
后,Product
實(shí)體將自動(dòng)擁有createdDate
和lastModifiedDate
字段,這些字段會(huì)在實(shí)體創(chuàng)建和更新時(shí)自動(dòng)填充。
3.2 不使用基類(lèi)的審計(jì)實(shí)現(xiàn)
如果因?yàn)槟承┰虿幌胧褂美^承,也可以直接在實(shí)體類(lèi)中使用審計(jì)注解:
package com.example.entity; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; import java.time.LocalDateTime; /** * 訂單實(shí)體類(lèi) */ @Entity @Table(name = "tb_order") @EntityListeners(AuditingEntityListener.class) // 直接在實(shí)體類(lèi)上注冊(cè)監(jiān)聽(tīng)器 public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "order_number", nullable = false, unique = true) private String orderNumber; @Column(name = "customer_id") private Long customerId; @Column(name = "total_amount", precision = 10, scale = 2) private BigDecimal totalAmount; @Column(name = "status") private String status; @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; // Getter和Setter方法省略 }
3.3 自定義日期時(shí)間提供者
如果需要自定義日期時(shí)間的提供方式,可以實(shí)現(xiàn)DateTimeProvider
接口:
package com.example.audit; import org.springframework.data.auditing.DateTimeProvider; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.TemporalAccessor; import java.util.Optional; /** * 自定義日期時(shí)間提供者 */ @Component public class CustomDateTimeProvider implements DateTimeProvider { @Override public Optional<TemporalAccessor> getNow() { // 使用特定時(shí)區(qū)的當(dāng)前時(shí)間 return Optional.of(LocalDateTime.now(ZoneId.of("Asia/Shanghai"))); } }
然后在配置類(lèi)中注冊(cè)這個(gè)提供者:
package com.example.config; import com.example.audit.CustomDateTimeProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.auditing.DateTimeProvider; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; /** * JPA審計(jì)功能配置類(lèi) */ @Configuration @EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider") // 指定日期時(shí)間提供者 public class JpaAuditingConfig { @Bean public DateTimeProvider dateTimeProvider() { return new CustomDateTimeProvider(); } }
四、實(shí)現(xiàn)用戶(hù)審計(jì)
除了時(shí)間審計(jì),還可以實(shí)現(xiàn)用戶(hù)審計(jì),即記錄創(chuàng)建和修改實(shí)體的用戶(hù)。
4.1 擴(kuò)展審計(jì)基類(lèi)
擴(kuò)展審計(jì)基類(lèi),添加用戶(hù)審計(jì)字段:
package com.example.entity; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; /** * 包含完整審計(jì)字段的基礎(chǔ)實(shí)體類(lèi) */ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class FullAuditableEntity { @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; @CreatedBy @Column(name = "created_by", updatable = false) private String createdBy; @LastModifiedBy @Column(name = "last_modified_by") private String lastModifiedBy; // Getter和Setter方法省略 }
4.2 實(shí)現(xiàn)AuditorAware接口
要使用@CreatedBy
和@LastModifiedBy
注解,需要實(shí)現(xiàn)AuditorAware
接口,提供當(dāng)前用戶(hù)信息:
package com.example.audit; import org.springframework.data.domain.AuditorAware; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import java.util.Optional; /** * 當(dāng)前用戶(hù)提供者 */ @Component public class SpringSecurityAuditorAware implements AuditorAware<String> { @Override public Optional<String> getCurrentAuditor() { // 從Spring Security上下文中獲取當(dāng)前用戶(hù) Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { return Optional.of("anonymousUser"); } return Optional.of(authentication.getName()); } }
然后在配置類(lèi)中注冊(cè)這個(gè)提供者:
package com.example.config; import com.example.audit.CustomDateTimeProvider; import com.example.audit.SpringSecurityAuditorAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.auditing.DateTimeProvider; import org.springframework.data.domain.AuditorAware; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; /** * JPA審計(jì)功能配置類(lèi) */ @Configuration @EnableJpaAuditing( dateTimeProviderRef = "dateTimeProvider", auditorAwareRef = "auditorAware" // 指定用戶(hù)提供者 ) public class JpaAuditingConfig { @Bean public DateTimeProvider dateTimeProvider() { return new CustomDateTimeProvider(); } @Bean public AuditorAware<String> auditorAware() { return new SpringSecurityAuditorAware(); } }
4.3 使用完整審計(jì)實(shí)體
創(chuàng)建業(yè)務(wù)實(shí)體并繼承完整審計(jì)基類(lèi):
package com.example.entity; import javax.persistence.*; import java.math.BigDecimal; /** * 訂單實(shí)體類(lèi) */ @Entity @Table(name = "tb_order") public class Order extends FullAuditableEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "order_number", nullable = false, unique = true) private String orderNumber; @Column(name = "customer_id") private Long customerId; @Column(name = "total_amount", precision = 10, scale = 2) private BigDecimal totalAmount; @Column(name = "status") private String status; // Getter和Setter方法省略 }
五、實(shí)際應(yīng)用場(chǎng)景
Spring Data JPA的審計(jì)功能在實(shí)際開(kāi)發(fā)中有廣泛的應(yīng)用場(chǎng)景。
5.1 數(shù)據(jù)版本控制
結(jié)合版本控制字段,實(shí)現(xiàn)樂(lè)觀鎖和數(shù)據(jù)版本追蹤:
package com.example.entity; import javax.persistence.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.time.LocalDateTime; /** * 文檔實(shí)體類(lèi) */ @Entity @Table(name = "tb_document") @EntityListeners(AuditingEntityListener.class) public class Document { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "title") private String title; @Column(name = "content", columnDefinition = "TEXT") private String content; @Version // 版本控制字段 @Column(name = "version") private Long version; @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; // Getter和Setter方法省略 }
5.2 審計(jì)日志記錄
利用實(shí)體監(jiān)聽(tīng)器,實(shí)現(xiàn)更詳細(xì)的審計(jì)日志記錄:
package com.example.listener; import com.example.entity.AuditLog; import com.example.entity.Product; import com.example.repository.AuditLogRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.persistence.PostPersist; import javax.persistence.PostUpdate; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import java.time.LocalDateTime; /** * 產(chǎn)品審計(jì)監(jiān)聽(tīng)器 */ @Component public class ProductAuditListener { @Autowired private AuditLogRepository auditLogRepository; private static final ThreadLocal<Product> originalState = new ThreadLocal<>(); @PrePersist public void prePersist(Product product) { // 新建產(chǎn)品前的操作 } @PostPersist public void postPersist(Product product) { // 記錄產(chǎn)品創(chuàng)建日志 AuditLog log = new AuditLog(); log.setEntityType("Product"); log.setEntityId(product.getId().toString()); log.setAction("CREATE"); log.setTimestamp(LocalDateTime.now()); log.setDetails("Created product: " + product.getName()); auditLogRepository.save(log); } @PreUpdate public void preUpdate(Product product) { // 保存產(chǎn)品原始狀態(tài) Product original = new Product(); // 復(fù)制product的屬性到original originalState.set(original); } @PostUpdate public void postUpdate(Product product) { // 獲取原始狀態(tài) Product original = originalState.get(); // 記錄產(chǎn)品更新日志 AuditLog log = new AuditLog(); log.setEntityType("Product"); log.setEntityId(product.getId().toString()); log.setAction("UPDATE"); log.setTimestamp(LocalDateTime.now()); // 構(gòu)建變更信息 StringBuilder changes = new StringBuilder(); if (!product.getName().equals(original.getName())) { changes.append("Name changed from '") .append(original.getName()) .append("' to '") .append(product.getName()) .append("'. "); } // 其他字段變更檢查... log.setDetails(changes.toString()); auditLogRepository.save(log); // 清理ThreadLocal originalState.remove(); } }
要啟用這個(gè)監(jiān)聽(tīng)器,需要在Product實(shí)體上注冊(cè):
@Entity @Table(name = "tb_product") @EntityListeners({AuditingEntityListener.class, ProductAuditListener.class}) public class Product extends AuditableEntity { // 實(shí)體內(nèi)容 }
5.3 多租戶(hù)審計(jì)
在多租戶(hù)系統(tǒng)中,結(jié)合審計(jì)功能實(shí)現(xiàn)租戶(hù)數(shù)據(jù)隔離:
package com.example.entity; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; /** * 多租戶(hù)審計(jì)基類(lèi) */ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public abstract class TenantAuditableEntity { @Column(name = "tenant_id", nullable = false, updatable = false) private String tenantId; @CreatedDate @Column(name = "created_date", updatable = false) private LocalDateTime createdDate; @LastModifiedDate @Column(name = "last_modified_date") private LocalDateTime lastModifiedDate; @CreatedBy @Column(name = "created_by", updatable = false) private String createdBy; @LastModifiedBy @Column(name = "last_modified_by") private String lastModifiedBy; // Getter和Setter方法省略 }
使用多租戶(hù)審計(jì)實(shí)體:
package com.example.entity; import javax.persistence.*; /** * 客戶(hù)實(shí)體類(lèi) */ @Entity @Table(name = "tb_customer") public class Customer extends TenantAuditableEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name") private String name; @Column(name = "email") private String email; @Column(name = "phone") private String phone; // Getter和Setter方法省略 }
六、高級(jí)技巧
Spring Data JPA審計(jì)功能還有一些高級(jí)用法,可以滿(mǎn)足更復(fù)雜的審計(jì)需求。
6.1 條件審計(jì)
有時(shí)候我們只希望在特定條件下進(jìn)行審計(jì)。可以通過(guò)自定義實(shí)體監(jiān)聽(tīng)器實(shí)現(xiàn):
package com.example.listener; import com.example.entity.User; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import java.lang.reflect.Field; import java.time.LocalDateTime; /** * 條件審計(jì)監(jiān)聽(tīng)器 */ public class ConditionalAuditListener { @PrePersist public void touchForCreate(Object target) { // 只對(duì)激活狀態(tài)的用戶(hù)進(jìn)行審計(jì) if (target instanceof User) { User user = (User) target; if (user.isActive()) { setCreatedDate(user); } } } @PreUpdate public void touchForUpdate(Object target) { // 只對(duì)激活狀態(tài)的用戶(hù)進(jìn)行審計(jì) if (target instanceof User) { User user = (User) target; if (user.isActive()) { setLastModifiedDate(user); } } } private void setCreatedDate(Object target) { setFieldValue(target, CreatedDate.class, LocalDateTime.now()); } private void setLastModifiedDate(Object target) { setFieldValue(target, LastModifiedDate.class, LocalDateTime.now()); } private void setFieldValue(Object target, Class annotation, Object value) { try { for (Field field : target.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(annotation)) { field.setAccessible(true); field.set(target, value); } } } catch (IllegalAccessException e) { throw new RuntimeException("Failed to set auditing field", e); } } }
6.2 自定義審計(jì)注解
可以創(chuàng)建自定義審計(jì)注解,實(shí)現(xiàn)更靈活的審計(jì)邏輯:
package com.example.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義審計(jì)注解:記錄字段的歷史值 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface TrackChanges { String value() default ""; }
然后實(shí)現(xiàn)對(duì)應(yīng)的監(jiān)聽(tīng)器處理這個(gè)注解:
package com.example.listener; import com.example.annotation.TrackChanges; import com.example.entity.FieldChangeLog; import com.example.repository.FieldChangeLogRepository; import org.springframework.beans.factory.annotation.Autowired; import javax.persistence.PreUpdate; import java.lang.reflect.Field; import java.time.LocalDateTime; import java.util.Objects; /** * 字段變更跟蹤監(jiān)聽(tīng)器 */ public class FieldChangeTrackingListener { @Autowired private FieldChangeLogRepository changeLogRepository; @PreUpdate public void preUpdate(Object entity) { try { // 獲取實(shí)體ID Long entityId = getEntityId(entity); String entityType = entity.getClass().getSimpleName(); // 查找標(biāo)記了@TrackChanges的字段 for (Field field : entity.getClass().getDeclaredFields()) { TrackChanges annotation = field.getAnnotation(TrackChanges.class); if (annotation != null) { field.setAccessible(true); // 獲取字段新值 Object newValue = field.get(entity); // 從數(shù)據(jù)庫(kù)獲取原始實(shí)體和字段舊值 Object originalEntity = loadOriginalEntity(entityId, entity.getClass()); field.setAccessible(true); Object oldValue = field.get(originalEntity); // 如果值發(fā)生變化,記錄日志 if (!Objects.equals(oldValue, newValue)) { FieldChangeLog changeLog = new FieldChangeLog(); changeLog.setEntityType(entityType); changeLog.setEntityId(entityId); changeLog.setFieldName(field.getName()); changeLog.setOldValue(oldValue != null ? oldValue.toString() : null); changeLog.setNewValue(newValue != null ? newValue.toString() : null); changeLog.setChangedAt(LocalDateTime.now()); changeLogRepository.save(changeLog); } } } } catch (Exception e) { throw new RuntimeException("Failed to track field changes", e); } } private Long getEntityId(Object entity) throws Exception { // 獲取實(shí)體ID的邏輯 // ... return null; } private Object loadOriginalEntity(Long entityId, Class<?> entityClass) { // 從數(shù)據(jù)庫(kù)加載原始實(shí)體的邏輯 // ... return null; } }
總結(jié)
Spring Data JPA的審計(jì)功能提供了一種強(qiáng)大而靈活的機(jī)制,用于自動(dòng)跟蹤實(shí)體的創(chuàng)建和修改信息。通過(guò)使用@CreatedDate和@LastModifiedDate注解,開(kāi)發(fā)者可以輕松地實(shí)現(xiàn)時(shí)間審計(jì);結(jié)合@CreatedBy和@LastModifiedBy注解以及AuditorAware接口,還可以實(shí)現(xiàn)用戶(hù)審計(jì)。這些功能大大簡(jiǎn)化了審計(jì)系統(tǒng)的開(kāi)發(fā)工作,使開(kāi)發(fā)者能夠?qū)W⒂跇I(yè)務(wù)邏輯的實(shí)現(xiàn)。
在實(shí)際應(yīng)用中,審計(jì)功能可以與版本控制、詳細(xì)日志記錄和多租戶(hù)系統(tǒng)等場(chǎng)景結(jié)合,滿(mǎn)足不同的業(yè)務(wù)需求。通過(guò)自定義實(shí)體監(jiān)聽(tīng)器和審計(jì)注解,還可以實(shí)現(xiàn)更加復(fù)雜和靈活的審計(jì)邏輯??傊琒pring Data JPA的審計(jì)功能是構(gòu)建健壯企業(yè)級(jí)應(yīng)用的重要組成部分,對(duì)于提高系統(tǒng)的可追溯性和安全性具有重要意義。
到此這篇關(guān)于SpringData JPA審計(jì)功能(@CreatedDate與@LastModifiedDate)實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringData JPA審計(jì)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程利用socket多線程訪問(wèn)服務(wù)器文件代碼示例
這篇文章主要介紹了Java編程利用socket多線程訪問(wèn)服務(wù)器文件代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程
這篇文章主要介紹了從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08nacos配置中心遠(yuǎn)程調(diào)用讀取不到配置文件的解決
這篇文章主要介紹了nacos配置中心遠(yuǎn)程調(diào)用讀取不到配置文件的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2022-01-01一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J
本文主要介紹了一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03Java網(wǎng)絡(luò)編程之URL+URLconnection使用方法示例
這篇文章主要介紹了Java網(wǎng)絡(luò)編程之URL+URLconnection使用方法示例,還是比較不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-11-11基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲(chóng)代碼
這篇文章主要介紹了基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲(chóng)代碼的相關(guān)資料,需要的朋友可以參考下2015-12-12SpringCloud Zuul和Gateway的實(shí)例代碼(搭建方式)
本文主要介紹了SpringCloudZuul和SpringCloudGateway的簡(jiǎn)單示例,SpringCloudGateway是推薦使用的API網(wǎng)關(guān)解決方案,基于SpringFramework5和ProjectReactor構(gòu)建,具有更高的性能和吞吐量2025-02-02使用JSCH框架通過(guò)跳轉(zhuǎn)機(jī)訪問(wèn)其他節(jié)點(diǎn)的方法
下面小編就為大家分享一篇使用JSCH框架通過(guò)跳轉(zhuǎn)機(jī)訪問(wèn)其他節(jié)點(diǎn)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12