SpringData JPA實體映射與關系映射的實現(xiàn)
引言
在企業(yè)級Java應用開發(fā)中,對象關系映射(ORM)技術已成為連接面向?qū)ο缶幊毯完P系型數(shù)據(jù)庫的重要橋梁。Spring Data JPA作為Spring生態(tài)系統(tǒng)中的核心項目,通過JPA規(guī)范提供了優(yōu)雅而強大的實體映射與關系處理機制。本文深入探討Spring Data JPA的實體映射和關系映射技術,幫助開發(fā)者掌握如何將Java對象與數(shù)據(jù)庫表之間建立有效的映射關系,提高開發(fā)效率并保證數(shù)據(jù)訪問層的可維護性。
一、實體映射基礎
實體映射是JPA的核心功能,它定義了Java對象與數(shù)據(jù)庫表之間的映射關系。在Spring Data JPA中,通過注解驅(qū)動的方式可以輕松完成這種映射,無需編寫復雜的XML配置。
1.1 基本實體映射
一個基本的實體類需要使用@Entity注解標記,并通過@Table指定對應的數(shù)據(jù)庫表名:
package com.example.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 用戶實體類
* 演示基本實體映射
*/
@Entity
@Table(name = "tb_user")
public class User {
@Id // 標記主鍵
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主鍵策略
private Long id;
@Column(name = "username", length = 50, nullable = false, unique = true)
private String username;
@Column(name = "email", length = 100)
private String email;
@Column(name = "password", length = 64)
private String password;
@Column(name = "age")
private Integer age;
@Column(name = "active")
private Boolean active = true;
@Column(name = "created_at")
private LocalDateTime createdAt;
// 構造函數(shù)、getter和setter方法省略
}
1.2 實體屬性映射
JPA提供了豐富的屬性映射注解,可以精確控制列的特性:
package com.example.entity;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 產(chǎn)品實體類
* 演示更復雜的屬性映射
*/
@Entity
@Table(name = "tb_product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(name = "product_seq", sequenceName = "product_sequence", allocationSize = 1)
private Long id;
@Column(name = "product_name", length = 100, nullable = false)
private String name;
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@Column(name = "price", precision = 10, scale = 2)
private BigDecimal price;
@Column(name = "stock_quantity")
private Integer stockQuantity;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private ProductStatus status;
@Temporal(TemporalType.DATE)
@Column(name = "release_date")
private java.util.Date releaseDate;
@Transient // 不映射到數(shù)據(jù)庫
private BigDecimal discountPrice;
@Lob // 大對象
@Column(name = "image_data")
private byte[] imageData;
// 產(chǎn)品狀態(tài)枚舉
public enum ProductStatus {
DRAFT, PUBLISHED, OUT_OF_STOCK, DISCONTINUED
}
// 構造函數(shù)、getter和setter方法省略
}
1.3 復合主鍵映射
有時實體需要使用復合主鍵,JPA提供了兩種方式處理:@IdClass和@EmbeddedId:
package com.example.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 訂單項實體類
* 演示復合主鍵映射(使用@IdClass)
*/
@Entity
@Table(name = "tb_order_item")
@IdClass(OrderItemId.class)
public class OrderItem {
@Id
@Column(name = "order_id")
private Long orderId;
@Id
@Column(name = "product_id")
private Long productId;
@Column(name = "quantity", nullable = false)
private Integer quantity;
@Column(name = "unit_price", nullable = false)
private Double unitPrice;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 訂單項復合主鍵類
*/
class OrderItemId implements Serializable {
private Long orderId;
private Long productId;
// 默認構造函數(shù)
public OrderItemId() {}
// 帶參構造函數(shù)
public OrderItemId(Long orderId, Long productId) {
this.orderId = orderId;
this.productId = productId;
}
// equals和hashCode方法實現(xiàn)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
OrderItemId that = (OrderItemId) o;
return Objects.equals(orderId, that.orderId) &&
Objects.equals(productId, that.productId);
}
@Override
public int hashCode() {
return Objects.hash(orderId, productId);
}
// getter和setter方法省略
}
使用@EmbeddedId的方式:
package com.example.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Objects;
/**
* 學生課程成績實體類
* 演示復合主鍵映射(使用@EmbeddedId)
*/
@Entity
@Table(name = "tb_student_course")
public class StudentCourse {
@EmbeddedId
private StudentCourseId id;
@Column(name = "score")
private Double score;
@Column(name = "semester")
private String semester;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 學生課程復合主鍵類
*/
@Embeddable
class StudentCourseId implements Serializable {
@Column(name = "student_id")
private Long studentId;
@Column(name = "course_id")
private Long courseId;
// 默認構造函數(shù)
public StudentCourseId() {}
// 帶參構造函數(shù)
public StudentCourseId(Long studentId, Long courseId) {
this.studentId = studentId;
this.courseId = courseId;
}
// equals和hashCode方法實現(xiàn)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StudentCourseId that = (StudentCourseId) o;
return Objects.equals(studentId, that.studentId) &&
Objects.equals(courseId, that.courseId);
}
@Override
public int hashCode() {
return Objects.hash(studentId, courseId);
}
// getter和setter方法省略
}
二、關系映射詳解
關系映射是JPA的另一核心功能,它處理實體之間的關聯(lián)關系,對應于數(shù)據(jù)庫表之間的外鍵關系。JPA支持一對一、一對多、多對一和多對多四種關系類型。
2.1 一對一關系(@OneToOne)
一對一關系是兩個實體之間最簡單的關聯(lián),每個實體實例只關聯(lián)另一個實體的一個實例:
package com.example.entity;
import javax.persistence.*;
/**
* 用戶詳情實體類
* 演示與User的一對一關系
*/
@Entity
@Table(name = "tb_user_profile")
public class UserProfile {
@Id
private Long id;
@Column(name = "phone_number")
private String phoneNumber;
@Column(name = "address")
private String address;
@Column(name = "bio", columnDefinition = "TEXT")
private String bio;
@OneToOne
@MapsId // 使用User的ID作為主鍵
@JoinColumn(name = "user_id")
private User user;
// 構造函數(shù)、getter和setter方法省略
}
// 在User類中添加對UserProfile的引用
@Entity
@Table(name = "tb_user")
public class User {
// 之前的字段...
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY,
orphanRemoval = true)
private UserProfile profile;
// 構造函數(shù)、getter和setter方法省略
}
2.2 一對多關系(@OneToMany)和多對一關系(@ManyToOne)
一對多和多對一是相互對應的關系,表示一個實體實例可以關聯(lián)另一個實體的多個實例:
package com.example.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 訂單實體類
* 演示與OrderItem的一對多關系
*/
@Entity
@Table(name = "tb_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number", unique = true)
private String orderNumber;
@Column(name = "order_date")
private LocalDateTime orderDate;
@Column(name = "total_amount")
private Double totalAmount;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private OrderStatus status;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
// 訂單狀態(tài)枚舉
public enum OrderStatus {
PENDING, PAID, SHIPPED, DELIVERED, CANCELLED
}
// 添加訂單項的便捷方法
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}
// 移除訂單項的便捷方法
public void removeItem(OrderItem item) {
items.remove(item);
item.setOrder(null);
}
// 構造函數(shù)、getter和setter方法省略
}
/**
* 訂單項實體類
* 演示與Order的多對一關系
*/
@Entity
@Table(name = "tb_order_item")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
private Product product;
@Column(name = "quantity")
private Integer quantity;
@Column(name = "unit_price")
private Double unitPrice;
// 構造函數(shù)、getter和setter方法省略
}
2.3 多對多關系(@ManyToMany)
多對多關系需要一個中間表來存儲關聯(lián)關系:
package com.example.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
/**
* 學生實體類
* 演示與Course的多對多關系
*/
@Entity
@Table(name = "tb_student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "student_name", nullable = false)
private String name;
@Column(name = "student_number", unique = true)
private String studentNumber;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "tb_student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
// 添加課程的便捷方法
public void addCourse(Course course) {
courses.add(course);
course.getStudents().add(this);
}
// 移除課程的便捷方法
public void removeCourse(Course course) {
courses.remove(course);
course.getStudents().remove(this);
}
// 構造函數(shù)、getter和setter方法省略
}
/**
* 課程實體類
* 演示與Student的多對多關系
*/
@Entity
@Table(name = "tb_course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "course_name", nullable = false)
private String name;
@Column(name = "course_code", unique = true)
private String code;
@Column(name = "credits")
private Integer credits;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
// 構造函數(shù)、getter和setter方法省略
}
三、繼承映射策略
JPA提供了三種實體繼承映射策略,用于處理類繼承層次結構到數(shù)據(jù)庫表的映射。
3.1 單表策略(SINGLE_TABLE)
單表策略是默認策略,將整個繼承層次結構映射到單個表:
package com.example.entity;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* 支付記錄抽象基類
* 演示單表繼承策略
*/
@Entity
@Table(name = "tb_payment")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "payment_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Payment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "amount")
private BigDecimal amount;
@Column(name = "payment_date")
private java.time.LocalDateTime paymentDate;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 信用卡支付實體類
*/
@Entity
@DiscriminatorValue("CREDIT_CARD")
public class CreditCardPayment extends Payment {
@Column(name = "card_number")
private String cardNumber;
@Column(name = "card_holder")
private String cardHolder;
@Column(name = "expiry_date")
private String expiryDate;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 銀行轉賬支付實體類
*/
@Entity
@DiscriminatorValue("BANK_TRANSFER")
public class BankTransferPayment extends Payment {
@Column(name = "bank_name")
private String bankName;
@Column(name = "account_number")
private String accountNumber;
@Column(name = "reference_number")
private String referenceNumber;
// 構造函數(shù)、getter和setter方法省略
}
3.2 連接表策略(JOINED)
連接表策略為每個子類創(chuàng)建一個表,通過外鍵關聯(lián)到父類表:
package com.example.entity;
import javax.persistence.*;
/**
* 人員抽象基類
* 演示連接表繼承策略
*/
@Entity
@Table(name = "tb_person")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Person {
@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;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 員工實體類
*/
@Entity
@Table(name = "tb_employee")
@PrimaryKeyJoinColumn(name = "person_id")
public class Employee extends Person {
@Column(name = "employee_number")
private String employeeNumber;
@Column(name = "department")
private String department;
@Column(name = "position")
private String position;
@Column(name = "salary")
private Double salary;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 客戶實體類
*/
@Entity
@Table(name = "tb_customer")
@PrimaryKeyJoinColumn(name = "person_id")
public class Customer extends Person {
@Column(name = "customer_number")
private String customerNumber;
@Column(name = "company")
private String company;
@Column(name = "industry")
private String industry;
// 構造函數(shù)、getter和setter方法省略
}
3.3 表格每類策略(TABLE_PER_CLASS)
表格每類策略為每個具體類創(chuàng)建一個獨立的表:
package com.example.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 通知抽象基類
* 演示表格每類繼承策略
*/
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Notification {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "title")
private String title;
@Column(name = "content")
private String content;
@Column(name = "sent_at")
private LocalDateTime sentAt;
@Column(name = "is_read")
private boolean read;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 郵件通知實體類
*/
@Entity
@Table(name = "tb_email_notification")
public class EmailNotification extends Notification {
@Column(name = "email_address")
private String emailAddress;
@Column(name = "subject")
private String subject;
@Column(name = "cc")
private String cc;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 短信通知實體類
*/
@Entity
@Table(name = "tb_sms_notification")
public class SmsNotification extends Notification {
@Column(name = "phone_number")
private String phoneNumber;
@Column(name = "sender")
private String sender;
// 構造函數(shù)、getter和setter方法省略
}
四、實體監(jiān)聽器與回調(diào)
JPA提供了實體生命周期事件回調(diào)機制,允許在實體狀態(tài)變更時執(zhí)行自定義邏輯。這些回調(diào)可以用于實現(xiàn)審計、驗證或其他橫切關注點。
package com.example.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 實體基類
* 演示實體監(jiān)聽器與回調(diào)
*/
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "created_by", updatable = false)
private String createdBy;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Column(name = "updated_by")
private String updatedBy;
@PrePersist
public void prePersist() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
// 可以從SecurityContext獲取當前用戶
String currentUser = getCurrentUser();
createdBy = currentUser;
updatedBy = currentUser;
}
@PreUpdate
public void preUpdate() {
updatedAt = LocalDateTime.now();
updatedBy = getCurrentUser();
}
// 獲取當前用戶的輔助方法
private String getCurrentUser() {
// 實際應用中,這里可以集成Spring Security獲取當前用戶
return "system"; // 示例返回默認值
}
// getter和setter方法省略
}
/**
* 自定義審計監(jiān)聽器
*/
public class AuditingEntityListener {
@PrePersist
public void touchForCreate(Object entity) {
if (entity instanceof Auditable) {
((Auditable) entity).setCreatedAt(LocalDateTime.now());
}
}
@PreUpdate
public void touchForUpdate(Object entity) {
if (entity instanceof Auditable) {
((Auditable) entity).setUpdatedAt(LocalDateTime.now());
}
}
}
/**
* 可審計接口
*/
public interface Auditable {
void setCreatedAt(LocalDateTime dateTime);
void setUpdatedAt(LocalDateTime dateTime);
}
五、實體映射最佳實踐
在SpringData JPA項目中,遵循一些最佳實踐可以提高代碼質(zhì)量和性能:
5.1 使用合適的關系加載策略
關系加載策略對性能有重大影響,根據(jù)業(yè)務需求選擇合適的加載方式:
package com.example.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
/**
* 部門實體類
* 演示不同加載策略的使用
*/
@Entity
@Table(name = "tb_department")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false)
private String name;
// 部門經(jīng)理是經(jīng)常訪問的,使用EAGER加載
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "manager_id")
private Employee manager;
// 部門可能有很多員工,使用LAZY加載避免一次性加載大量數(shù)據(jù)
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
private Set<Employee> employees = new HashSet<>();
// 部門所屬公司信息經(jīng)常需要訪問,但使用LAZY加載并通過關聯(lián)關系圖優(yōu)化
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id")
private Company company;
// 構造函數(shù)、getter和setter方法省略
}
5.2 使用索引和約束
利用數(shù)據(jù)庫索引和約束提高查詢性能和數(shù)據(jù)完整性:
package com.example.entity;
import javax.persistence.*;
/**
* 產(chǎn)品庫存實體類
* 演示索引和約束的使用
*/
@Entity
@Table(
name = "tb_product_inventory",
indexes = {
@Index(name = "idx_warehouse_product", columnList = "warehouse_id, product_id"),
@Index(name = "idx_product_stock", columnList = "product_id, stock_quantity")
},
uniqueConstraints = {
@UniqueConstraint(name = "uk_warehouse_product", columnNames = {"warehouse_id", "product_id"})
}
)
public class ProductInventory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "warehouse_id", nullable = false)
private Warehouse warehouse;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private Product product;
@Column(name = "stock_quantity", nullable = false)
private Integer stockQuantity;
@Column(name = "min_stock_level")
private Integer minStockLevel;
@Column(name = "max_stock_level")
private Integer maxStockLevel;
// 構造函數(shù)、getter和setter方法省略
}
5.3 使用嵌入類型
對于經(jīng)常一起使用的相關屬性,可以使用嵌入類型提高代碼可讀性:
package com.example.entity;
import javax.persistence.*;
/**
* 地址嵌入類型
*/
@Embeddable
public class Address {
@Column(name = "street")
private String street;
@Column(name = "city")
private String city;
@Column(name = "state")
private String state;
@Column(name = "postal_code")
private String postalCode;
@Column(name = "country")
private String country;
// 構造函數(shù)、getter和setter方法省略
}
/**
* 客戶實體類
* 演示嵌入類型的使用
*/
@Entity
@Table(name = "tb_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false)
private String name;
// 嵌入賬單地址
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "billing_street")),
@AttributeOverride(name = "city", column = @Column(name = "billing_city")),
@AttributeOverride(name = "state", column = @Column(name = "billing_state")),
@AttributeOverride(name = "postalCode", column = @Column(name = "billing_postal_code")),
@AttributeOverride(name = "country", column = @Column(name = "billing_country"))
})
private Address billingAddress;
// 嵌入配送地址
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "shipping_street")),
@AttributeOverride(name = "city", column = @Column(name = "shipping_city")),
@AttributeOverride(name = "state", column = @Column(name = "shipping_state")),
@AttributeOverride(name = "postalCode", column = @Column(name = "shipping_postal_code")),
@AttributeOverride(name = "country", column = @Column(name = "shipping_country"))
})
private Address shippingAddress;
// 構造函數(shù)、getter和setter方法省略
}
總結
Spring Data JPA的實體映射與關系映射能力為Java開發(fā)者提供了強大而靈活的數(shù)據(jù)持久化解決方案。通過合理使用實體映射、關系映射、繼承策略和生命周期回調(diào),開發(fā)者可以構建出既符合面向?qū)ο笤瓌t又高效利用關系型數(shù)據(jù)庫的應用。本文詳細介紹了基本實體映射、實體屬性映射、復合主鍵映射以及四種核心關系映射類型的實現(xiàn)方法,并探討了實體繼承的三種策略和實體生命周期事件的應用。在實際項目中,選擇合適的映射策略對于提高應用性能和可維護性至關重要。通過遵循本文提及的最佳實踐,開發(fā)者可以構建出高質(zhì)量的數(shù)據(jù)訪問層,為整個應用奠定
到此這篇關于SpringData JPA實體映射與關系映射的實現(xiàn)的文章就介紹到這了,更多相關SpringData JPA實體映射與關系映射內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot使用Validation包進行輸入?yún)?shù)校驗
Spring Boot 自帶的 spring-boot-starter-validation 包支持以標準注解的方式進行輸入?yún)?shù)校驗,本文即關注 spring-boot-starter-validation 包所涵蓋的標準注解的使用、校驗異常的捕獲與展示、分組校驗功能的使用,以及自定義校驗器的使用,需要的朋友可以參考下2024-05-05
java web用servlet監(jiān)聽器實現(xiàn)顯示在線人數(shù)
這篇文章主要為大家詳細介紹了java web用servlet監(jiān)聽器實現(xiàn)顯示在線人數(shù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-03-03
Spring中的NamespaceHandler接口及相關軟件包說明
這篇文章主要介紹了Spring中的NamespaceHandler接口及相關軟件包說明,NamespaceHandler 接口,DefaultBeanDefinitionDocumentReader 使用該接口來處理在spring xml 配置文件中自定義的命名空間,需要的朋友可以參考下2023-12-12
Java格式化小數(shù)并保留兩位小數(shù)的四種方法
Java中格式化小數(shù)并保留兩位小數(shù)的四種方法:使用DecimalFormat、String.format()、BigDecimal和NumberFormat,每種方法都有其適用場景和特點,文章通過代碼示例介紹的非常詳細,需要的朋友可以參考下2025-03-03
Spring Boot中使用@Scheduled和Quartz實現(xiàn)定時任務的詳細過程
Spring Boot提供了兩種常見的定時任務實現(xiàn)方式,一種是基于@Scheduled注解的簡單定時任務,另一種是功能更強大的Quartz框架,本文將介紹如何在Spring Boot中使用這兩種方式實現(xiàn)定時任務和調(diào)度功能,感興趣的朋友一起看看吧2025-08-08

