成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Data Jpa必殺技:12大實戰技巧讓開發 & 性能飆升300%

開發 前端
隨著時間的推移,系統的數據量也隨之增大,我們都遇到過一些常見的挑戰,比如查詢速度慢、管理復雜的關系、理解復雜的原生查詢或優化接口效率。很多人都會遇到這些問題,解決這些問題對于提升我們的開發流程至關重要。

環境:SpringBoot3.4.2

1. 簡介

在基于Spring Boot 項目開發中,有一個關鍵組件是 Spring Data JPA,這是一個在數據處理方面表現出色的 API。它在我們的開發過程中具有不可忽視的重要性。

它不僅僅是一個工具,而是一個強大的工具,能夠顯著提升我們的開發流程,讓我們有信心和能力高效地處理復雜的數據管理任務。

最常用的默認注解包括:

  • @Repository:用于標記或注解一個扮演數據訪問對象(DAO)角色的類,通常我們是不需要寫該注解的。
  • @Query:允許開發者使用原生查詢的注解。

隨著時間的推移,系統的數據量也隨之增大,我們都遇到過一些常見的挑戰,比如查詢速度慢、管理復雜的關系、理解復雜的原生查詢或優化接口效率。很多人都會遇到這些問題,解決這些問題對于提升我們的開發流程至關重要。

接下來,我們將由淺入深的詳細介紹Spring Data JPA實戰技巧開發。

2.實戰案例

2.1 基本概念

開發中,在定義接口時,我們通常可以繼承:Repository,CrudRepository,PagingAndSortingRepository以及JpaRepository接口。

Repository

Repository 是最基本的接口,通常不包含任何方法。它不提供任何功能,但作為 Spring Data JPA 中所有其他倉庫接口的基礎接口。

public interface UserRepository extends Repository<User, Long> {
  // 沒有預定義的方法
}

不建議使用該接口,因為它沒有提供任務功能,僅起到了標記作用。

CrudRepository

CrudRepository 接口提供了 CRUD 操作。如果你需要基本的數據訪問能力而不需要排序或分頁,該接口非常適合你。

public interface UserRepository extends CrudRepository<User, Long> {
  // 提供了基本的 CRUD 方法
}

常用的基本方法:

  • save(S entity):保存給定的實體
  • findById(ID id):通過 ID 檢索實體
  • existsById(ID id):返回是否存在具有給定 ID 的實體
  • findAll():返回所有實體
  • deleteById(ID id):刪除具有給定 ID 的實體

PagingAndSortingRepository

該接口添加了分頁和排序的方法。這在處理大型數據集并在頁面上顯示數據時非常有用。這個接口也包含了 CRUD 操作。

public interface UserRepository extends PagingAndSortingRepository<User, Long> {
  // CRUD 方法加上分頁和排序
}

這個接口的額外方法包括:

  • findAll(Pageable pageable):返回符合 Pageable 對象中提供的分頁限制的實體頁面。
  • findAll(Sort sort):返回按給定選項排序的所有實體。

JpaRepository

這個接口添加了 JPA 特定的方法,并提供了一套完整的 JPA 相關方法,如批量操作、自定義查詢和刷新控制。這使得它成為 JPA 應用程序中最強大和最靈活的選項。

public interface UserRepository extends JpaRepository<User, Long> {
  // 完整的 CRUD 方法、分頁、排序和 JPA 特定方法
}

這個接口提供了一套全面的 JPA 相關操作方法,允許你執行各種任務而無需額外的接口或自定義代碼。

使用 JpaRepository,你可以通過遵循 findBy 后跟屬性名的命名約定來定義自定義查詢方法。Spring Data JPA 將根據方法名自動生成查詢。

public interface UserRepository extends JpaRepository<User, Long> {
  // 通過 ID 查找實體
  Optional<User> findById(Long id) ;
  // 查找所有具有給定名稱的實體
  List<User> findByName(String name) ;
  // 查找年齡大于給定值的實體
  List<User> findByAgeGreaterThan(int age) ;
}

為什么選擇 JpaRepository?

  • 功能全面:它結合了 CRUD、分頁、排序和 JPA 特定操作在一個接口中。
  • 便捷性:通過提供所有必要的方法簡化了代碼。
  • 性能:通過批量操作和刷新控制優化了性能。
  • 靈活性:允許自定義查詢方法并支持 JPQL(Java 持久化查詢語言)和原生 SQL 查詢。

2.2 使用 Specification 和 Criteria Builder

在使用 Spring Data JPA 時,有時我們需要更復雜的查詢,這些查詢無法通過簡單的查詢方法輕松實現。這時,Specification 和 Criteria Builder 就派上了用場,它們允許你構建動態查詢并處理復雜的場景。

Specification

Specification 是 Spring Data JPA 中的一個函數式接口,用于基于 JPA 條件創建動態查詢。它提供了一種以編程方式構建查詢的方法。當查詢條件在編譯時未知時,它非常有用。

import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;


public class UserSpecification {
  public static Specification<MyEntity> hasName(String name) {
    return (Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
      return cb.equal(root.get("name"), name);
    };
  }
}

使用

private final UserRepository userRepository ;


Specification<User> spec = UserSpecification.hasName("Pack");
List<User> results = userRepository.findAll(spec) ;

Criteria Builder

Criteria Builder API 是 JPA 的一部分,允許創建類型安全的查詢。它提供了一種使用 Java 對象而不是硬編碼字符串來動態構建查詢的方式。如下示例:

@Service
public class UserService {
  
  @PersistenceContext
  private EntityManager em;


  public List<User> findByName(String name) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> root = query.from(User.class);
    // 構建查詢
    query.select(root).where(cb.equal(root.get("name"), name));
    return em.createQuery(query).getResultList();
  }
}

你可以結合多個條件來構建更復雜的查詢。例如,你可以使用 and 和 or 來組合條件。

public class UserSpecification {
  public static Specification<User> hasName(String name) {
    return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
      return cb.equal(root.get("name"), name);
    };
  }
  public static Specification<User> hasStatus(Integer status) {
    return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
      return cb.equal(root.get("status"), status);
    };
  }
  public static Specification<User> hasAgeGreaterThan(int age) {
    return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
      return cb.greaterThan(root.get("age"), age);
    };
  }
  public static Specification<User> hasNameAndStatus(String name, Integer status) {
    return (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
      Predicate namePredicate = cb.equal(root.get("name"), name);
      Predicate statusPredicate = cb.equal(root.get("status"), status);
      return cb.and(namePredicate, statusPredicate);
    };
  }
}
// 使用
Specification<User> spec = UserSpecification.hasNameAndStatus("Pack", 1) ;
List<User> results = userRepository.findAll(spec) ;

結合多個規范可以創建更靈活和可重用的查詢條件。

Specification<User> spec = Specification.where(UserSpecification.hasName("Pack"))
    .and(UserSpecification.hasStatus(1))
    .and(UserSpecification.hasAgeGreaterThan(25));
List<User> results = myEntityRepository.findAll(spec);

2.3 開發技巧

為了最大化 Spring Data JPA 的效用,遵循一些提示和技巧是至關重要的。這些可以幫助你優化應用程序、避免常見陷阱,并確保你的代碼是可維護和高效的。

使用懶加載

默認情況下,將實體關系設置為 FetchType.LAZY,這意味著相關實體在訪問之前不會從數據庫加載。雖然這可以節省資源,但如果處理不當,也可能導致 N+1 選擇問題。

最佳實踐:對于大型或很少訪問的關系,使用懶加載。對于頻繁訪問的關系,考慮使用迫切加載。

@Entity
public class Customer {
  @OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
  private List<Order> orders ;
}

優化查詢

能用一個構建良好的查詢完成任務時,就不要運行多個查詢。必要時,使用 JPQL、Criteria API 或原生查詢來優化性能。

最佳實踐:使用自定義查詢或規范將相關查詢合并為單個數據庫訪問操作。

@Query("SELECT e FROM User e JOIN FETCH e.orders WHERE e.name = :name")
List<MyEntity> findByNameWithOrders(@Param("name") String name);

利用緩存

緩存可以顯著提高應用程序的性能,通過減少數據庫命中次數。Spring 提供了與 Ehcache、Redis 等緩存解決方案的輕松集成。

最佳實踐:緩存不經常更改的頻繁訪問數據。

private final UserRepository userRepository ;


@Cacheable("users")
public List<User> findAll() {
  return userRepository.findAll();
}

批量處理

在保存或刪除多個實體時,批量處理可以減少數據庫往返次數并提高性能。

最佳實踐:使用 saveAll 進行批量插入,使用 deleteInBatch 進行批量刪除。

private final UserRepository userRepository ;


public void saveUsers(List<User> users) {
  userRepository.saveAll(users);
}
public void deleteUsers(List<User> users) {
  userRepository.deleteInBatch(users);
}

適當的使用事務

確保你的數據庫操作被正確地包裹在事務中,以維護數據完整性。使用 Spring 的 @Transactional 注解來管理事務。

最佳實踐:在服務層使用 @Transactional,以確保方法內的所有操作都是單個事務的一部分。

@Service
public class UserService {
  private final UserRepository userRepository ;
  @Transactional
  public void updateUsers(List<User> users) {
    for (User user: users) {
      userRepository.save(user) ;
    }
  }
}

save方法的內部是使用了 @Transactional 注解。

避免N+1問題

N+1 選擇問題發生在應用程序為了獲取 N 個實體的集合(每個實體都有自己的相關實體)而發出 N+1 個數據庫查詢時,這會嚴重影響性能。

最佳實踐:在你的 JPQL 查詢中使用 JOIN FETCH 來在單個查詢中獲取相關實體。

@Query("SELECT e FROM Customer e JOIN FETCH e.orders WHERE e.status = :status")
List<Customer> findByStatusWithOrders(@Param("status") Integer status);

日志記錄&監控

在開發過程中啟用 SQL 日志記錄,以了解 Hibernate 生成的查詢。這可以幫助識別和優化低效的查詢。

最佳實踐:使用日志記錄來監控 SQL 查詢和性能指標。

spring:
  jpa:
    show-sql=true
  properties
    hibernate:
      '[format_sql]': true
      # 慢SQL閾值
      '[log_slow_query]': 1000

使用投影

有時,你只需要幾個字段而不是整個實體。使用投影來僅選擇必要的數據。

最佳實踐:使用投影來僅獲取所需的字段,減少從數據庫傳輸的數據量。

public interface UserProjection {
  String getName();
  String getStatus();
}


@Query("SELECT e.name AS name, e.status AS status FROM User e WHERE e.age > :age")
List<UserProjection> queryUser(@Param("age") int age) ;

使用視圖

有時,你的選擇查詢會變得更加復雜。創建虛擬表或表視圖可以幫助簡化數據訪問。

最佳實踐:使用視圖可以簡化 SELECT 語句,減少復雜性并避免潛在錯誤。

審計功能

Spring Data 支持實體變更審計(創建者/修改者/時間),我們能通過注解非常方便的應用審計功能。通過 @CreatedBy 和 @LastModifiedBy 來獲取創建或修改實體的用戶,以及 @CreatedDate 和 @LastModifiedDate 來獲取更改發生的時間。

@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
  @CreatedDate
  private LocalDateTime createTime;
  @LastModifiedDate
  private LocalDateTime updateTime;
  @CreatedBy
  private String createdBy;
  @LastModifiedBy
  private String updatedBy;
}

接下來,還需要提供如下組件用來獲取用戶。

@Component
public class PackAuditorAware implements AuditorAware<String> {
  public Optional<String> getCurrentAuditor() {
    return Optional.of("pack") ;
  }
}

鎖機制

要指定查詢方法使用的鎖模式,可以在查詢方法上使用 @Lock 注解,具體示例如下:

public interface ProductRepository extends JpaRepository<Product, Long> {


  @Transactional
  @Lock(LockModeType.PESSIMISTIC_READ)
  public Product findByName(String name) ;
}

注意:這里我們需要 @Transactional 事務注解。

運行輸出SQL如下:

SELECT
  p1_0.id,
  p1_0.NAME,
  p1_0.price,
  p1_0.quantity 
FROM
  t_product p1_0 
WHERE
  p1_0.NAME =? FOR SHARE

自動添加了 FOR SHARE。

流式查詢

可以使用 Java 8 的 Stream<T> 作為返回類型來增量處理查詢方法的結果。并非將查詢結果包裝在 Stream 中,而是使用特定于數據存儲的方法來執行流處理,如以下示例所示:

@Query("select u from User u")
Stream<User> findUserByStream();

流可能會封裝底層特定于數據存儲的資源,因此在使用后必須關閉。您可以使用 close() 方法手動關閉流,或者使用 Java 7 中的 try-with-resources 塊,如下例所示:

try (Stream<User> stream = repository.findUserByStream()) {
  stream.forEach(…) ;
}


責任編輯:武曉燕 來源: Springboot實戰案例錦集
相關推薦

2024-08-09 12:11:07

2010-08-11 16:43:05

職場

2018-09-21 14:32:00

iPaas云應用部署

2010-08-24 14:57:33

外企職場

2011-06-27 14:56:49

SEO

2009-09-28 11:16:23

UPS電源

2013-12-18 11:34:17

云文件共享服務云文件同步服務BYOD

2025-04-07 02:33:00

項目開發Spring

2013-05-10 09:23:14

iPaaS混合云集成云集成

2021-02-02 10:55:09

等級保護2.0信息安全網絡安全

2009-07-22 15:02:18

2012-05-29 10:19:41

2012-05-22 09:06:25

2009-10-13 16:38:04

強行關閉VMware虛

2014-08-05 15:36:04

騰訊云CDN

2023-04-07 17:44:43

2011-06-24 17:23:30

網站優化

2009-01-03 09:14:00

2009-06-08 09:08:21

職場裁員必殺技

2012-03-06 11:25:40

ibmdw
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美性极品xxxx做受 | 日韩国产在线 | 国产成人在线视频 | 日韩精品在线播放 | 欧美中文字幕一区二区 | 黄网站免费观看 | 日韩欧美一区在线 | 麻豆一区二区三区精品视频 | 亚洲第一视频网 | 天天操综合网 | 国产成人精品午夜视频免费 | 久久99精品久久久久久琪琪 | 求个av网址 | 欧美日韩高清一区二区三区 | 在线观看亚洲专区 | 九九综合九九 | 国产精品自拍av | 久久国| 一区二区成人 | 日本成人一区二区 | 欧美成人一区二区三区 | 日韩精品一区二区三区中文在线 | 毛片免费看 | 国产高清在线精品一区二区三区 | 美国av片在线观看 | 国产精久久久 | 欧美一区二区三区视频 | 日韩精品中文字幕一区二区三区 | 国产亚洲网站 | 91久久久久久久久久久久久 | 午夜精品久久久久久久久久久久久 | 午夜成人免费视频 | 国产网站在线免费观看 | 特级做a爰片毛片免费看108 | 国产精品视频在线免费观看 | 精品成人av | aaaa网站| 免费人成在线观看网站 | 精品国产视频 | 欧美一级欧美一级在线播放 | 一区二区播放 |