成人免费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全家桶實戰案例源碼
相關推薦

2023-09-27 23:43:51

單元測試Spring

2025-03-03 08:49:59

2020-03-31 15:03:56

Spring Boot代碼Java

2022-07-15 08:52:10

代碼Java設計模式

2023-11-09 08:01:41

Spring緩存注解

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數據

2020-11-02 07:00:29

Spring Boo注解自動化

2023-06-02 16:24:46

SpringBootSSM

2023-09-21 10:31:06

人工智能模型

2020-06-11 09:00:27

SDN網絡架構網絡

2022-05-31 08:36:41

微服務網關鑒權

2022-11-10 09:57:24

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領導者遠程工作

2017-08-02 14:44:06

Spring Boot開發注解
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 热久久久久 | 一区二区三区四区在线免费观看 | 天天插日日操 | 91日日 | 精品成人在线 | 欧美亚洲视频 | 伦理一区二区 | 91五月婷蜜桃综合 | 亚洲视频精品在线 | 亚洲精品国产成人 | 毛片a级毛片免费播放100 | 久久中文字幕电影 | 久久久久一区 | 九色视频网 | 99视频网 | 成人国产精品久久久 | 精品久久久久久久久久久久久久 | 日韩在线视频一区二区三区 | 在线观看亚 | 免费看国产片在线观看 | 黄色网址在线免费观看 | 久久国产传媒 | 麻豆视频在线免费看 | 亚洲精品九九 | 精品一区二区三区不卡 | 综合色婷婷 | 欧美在线高清 | 午夜男人视频 | www.国产.com| 国产成人精品久久 | 性高朝久久久久久久3小时 av一区二区三区四区 | 国产一区二区三区久久久久久久久 | 天天操夜夜操 | 九九热这里 | 视频一区二区在线观看 | 日韩aⅴ视频 | 中文字幕高清av | 一区二区三区四区电影视频在线观看 | 亚洲国产精品成人久久久 | 国内激情av片 | 欧美mv日韩mv国产网站91进入 |