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

太炸了,三個注解!Spring Boot + JPA代碼量暴減60%

開發 前端
在Spring Boot結合JPA進行開發時,面對復雜查詢或動態過濾等常見場景,如果未能及時更新自身的技術知識儲備,就可能陷入編寫大量冗余代碼的困境。

環境:SpringBoot3.4.2

1. 簡介

在Spring Boot結合JPA進行開發時,面對復雜查詢或動態過濾等常見場景,如果未能及時更新自身的技術知識儲備,就可能陷入編寫大量冗余代碼的困境。如下問題:

1?? 查詢邏輯分散:動態條件查詢需在Service層手動拼接CriteriaBuilder或Specification,導致代碼臃腫且難以維護;2?? 重復計算邏輯:如計算總金額、統計關聯表數量等衍生字段,需在實體中編寫冗余的字段或通過DTO層重復查詢;3?? 硬編碼過濾:數據權限控制常通過全局攔截器或硬編碼SQL實現,缺乏靈活性且難以擴展;

這些痛點導致代碼量激增、維護成本高昂,且業務邏輯與數據庫操作強耦合。

解決方案

@Formula@SQLRestriction (@Where)@Filter三大注解直擊痛點:

@Formula:實體字段直接映射SQL表達式,替代重復計算邏輯;

@SQLRestriction:實體級原生SQL條件,簡化動態查詢;

@Filter:參數化過濾條件,支持會話級控制,告別硬編碼。

用好這3個強大的注解,能讓代碼量暴減60%,性能與可維護性雙提升!

接下來,我們將詳細的介紹這3個注解的詳細應用。

2. 實戰案例

2.1 @Formula

通過該注解你可以指定一個用原生SQL編寫的表達式,該表達式用于讀取屬性的值,而不是將該字段映射到數據庫中。@Formula映射定義了一個"派生"屬性,當從數據庫讀取實體時,該屬性的狀態是根據其他列和函數計算得出的。如下示例:

拼接字段值

private String name;
private BigDecimal price;


@Formula("(concat(name, '/', price))")
private String info ;

該示例中,并不會在數據庫中創建info字段,而是通過這里定義的表達式concat(name, '/', price)(concat數據庫函數)將name字段值與price字段值通過 "/" 拼接在一起。

當我們執行查詢時,sql輸出如下:

org.hibernate.SQL Line:135 - select p1_0.id,p1_0.deleted,
  (concat(p1_0.name, '/', p1_0.price)),p1_0.name,
  p1_0.price,p1_0.stock from product p1_0

將上面的表達式作為select的一部分進行查詢。

我們不僅僅可以寫表達式,我們還可以執行SQL語句。

SQL子句

@Formula("(select sum(s.sale_price * s.quantity) from sales_detail s where s.product_id = id)")
private BigDecimal salePrice ;

同樣的該salePrice字段并不會在數據庫中創建。當執行查詢時,SQL輸出如下:

SELECT
  p1_0.id,
  p1_0.deleted,(
  concat( p1_0.NAME, '/', p1_0.price )),
  p1_0.name,
  p1_0.price,(
  SELECT
    sum( s.sale_price * s.quantity ) 
  FROM
    sales_detail s 
  WHERE
    s.product_id = p1_0.id 
  ),
  p1_0.stock 
FROM
  product p1_0

表達式同樣作為select的一部分進行了子查詢。

對于這種子查詢,我們還是需要結合自己的場景來決定是否適合通過此種方式進行查詢。

2.2 @SQLRestriction

該注解可以指定一個用原生SQL編寫的約束條件,該約束條件將被添加到為實體或集合生成的SQL中。簡單說,就是可以在對當前實體查詢時動態添加查詢條件。

@Entity
@Table(name = "product")
@SQLRestriction("deleted = 0")
public class Product {
  // ...其它屬性
  
  /**0: 未刪除, 1: 已刪除*/
  @Column(columnDefinition = "int default 0")
  private Integer deleted ;
}

這里通過@SQLRestriction注解添加了 "deleted = 0",當我們對該實體Product進行查詢時都會在原來SQL中添加該條件。如下SQL執行:

SELECT
  p1_0.id,
  p1_0.deleted,
  p1_0.name,
  p1_0.price
  p1_0.stock 
FROM
  product p1_0 
WHERE
  (p1_0.deleted = 0)

我們不僅僅可以在實體類上添加,還可以在集合屬性上添加。

集合屬性上

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "product_id")
@SQLRestriction("deleted = 0")
private Set<ProductDetail> productDetails = new HashSet<>() ;


@Entity
public class ProductDetail {
  // ...


  /**0: 未刪除, 1: 已刪除*/
  @Column(columnDefinition = "int default 0")
  private Integer deleted ;
}

當我們通過Product實體查詢時,生成SQL如下:

圖片圖片

2.3 @Filter

@SQLRestriction 注解的問題在于,它僅允許我們指定一個不包含參數的靜態查詢,并且無法根據需求動態啟用或禁用它。@Filter 注解的作用與 @SQLRestriction 類似,但它還可以在會話(session)級別啟用或禁用,并且支持參數化。

@Entity
@Table(name = "product")
@FilterDef(name = "filterByDeletedAndStock", parameters = {
    @ParamDef(name = "state", type = Integer.class),
    @ParamDef(name = "stock", type = Integer.class)
})
@Filters({
    @Filter(name = "filterByDeletedAndStock", condition = "deleted=:state and stock >:stock")
})
public class Product {}

在這里,我們通過@FilterDef注解,定義了一個名為filterByDeletedAndStock過濾器,并且還定義了2個參數state和stock。

接著,我們通過@Filters注解,定義了具體的過濾條件,其中name是上面@FilterDef定義的名稱,condition則為執行時動態添加的條件。

要使用定義的@Filter條件,我們這里通過AOP的方式動態設置。

首先,我們定義一個注解,只有使用了該注解的方法,才會在執行之前開啟過濾過功能。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EnableFilter {
}

接下來,定義切面攔截使用了@EnableFilter注解的方法。

@Component
@Aspect
public class FilterAspect {
  @PersistenceContext
  private EntityManager entityManager;
  @Around("@annotation(com.pack.formula.annotation.EnableFilter)")
  public Object doProcess(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
      // 從其它地方獲取參數值
      int state = 0 ;
      int stock = 80 ;
      Filter filter = entityManager.unwrap(Session.class).enableFilter("filterByDeletedAndStock");
      filter.setParameter("state", state) ;
      filter.setParameter("stock", stock) ;
      return joinPoint.proceed();
    } catch (Throwable ex) {
      throw ex;
    } finally {
      entityManager.unwrap(Session.class).disableFilter("filterByDeletedAndStock") ;
    }
  }
}

我們這里是模擬,所以@Filter中定義的2個參數直接寫死了。

業務代碼

@EnableFilter
public List<Product> query() {
  return this.productRepository.findAll() ;
}

執行后生成的SQL如下:

圖片圖片

動態添加了查詢條件。

當我們沒有注解時,生成SQL如下:

圖片圖片

責任編輯:武曉燕 來源: Springboot全家桶實戰案例源碼
相關推薦

2025-03-03 08:49:59

2023-09-27 23:43:51

單元測試Spring

2020-03-31 15:03:56

Spring Boot代碼Java

2023-11-09 08:01:41

Spring緩存注解

2022-07-15 08:52:10

代碼Java設計模式

2025-02-26 08:03:17

SpringJPAMyBatis

2022-04-28 08:05:05

數據庫數據庫交互

2023-04-17 23:49:09

開發代碼Java

2024-03-07 12:51:44

代碼CRUD數據

2023-09-21 10:31:06

人工智能模型

2020-11-02 07:00:29

Spring Boo注解自動化

2023-06-02 16:24:46

SpringBootSSM

2020-06-11 09:00:27

SDN網絡架構網絡

2022-11-10 09:57:24

2022-05-31 08:36:41

微服務網關鑒權

2020-05-12 20:40:58

SQL慢查詢優化數據庫

2021-06-29 17:19:44

Spring Boot集成Flyway

2024-08-09 08:46:00

Springjar 包YAML

2023-04-26 11:14:11

IT領導者遠程工作

2024-12-20 07:30:00

C++17代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产9 9在线 | 中文 | 伊人性伊人情综合网 | 一区二区三区欧美 | 久久伊人青青草 | 欧美在线看片 | 玖玖免费 | 99热精品国产 | 日韩中文一区 | 日本一本视频 | 在线亚洲人成电影网站色www | 亚洲精品4 | 亚洲精品日韩欧美 | 成人小视频在线 | 久久乐国产精品 | 福利视频网站 | 国产日产精品一区二区三区四区 | 国产极品粉嫩美女呻吟在线看人 | 日日欧美 | 一级毛片,一级毛片 | 天天草天天爱 | 国产色| 精品一区国产 | 91电影在线播放 | av在线天堂网| 一区二区电影网 | 精品久久久久一区二区国产 | 欧美日韩亚洲国产 | 在线观看三级av | 欧美一级全黄 | 一区二区三区四区免费视频 | 我要看一级片 | 国产精品亚洲第一 | 欧美亚洲激情 | 91影院 | 国产高清亚洲 | 91九色视频在线 | www.狠狠干 | 久久精品国产亚洲 | 欧美中文字幕在线观看 | 日本高清精品 | 国产乱性 |