歡迎光臨
每天分享高質量文章

Hibernate5 與 Spring Boot2 最佳效能實踐(1)

(給ImportNew加星標,提高Java技能)

編譯:ImportNew/唐尤華

dzone.com/articles/50-best-performance-practices-for-hibernate-5-amp

 

1. 透過位元組碼增強實現屬性延遲載入

 

預設情況下,物體的屬性是立即載入的,即一次載入所有屬性。你確定這是你想要的嗎?

 

“描述:”即使目前沒有這樣的需求,瞭解可以延遲載入屬性也很重要。透過 Hibernate 位元組碼插裝或者 subentities 也可以實現。該特性對於儲存了大量 `CLOB`、`BLOB`、`VARBINARY` 型別資料時非常有用。

 

> 譯註:位元組碼增強(Bytecode enhancement)與位元組碼插裝(Bytecode instrumentation)的區別。位元組碼增強分線上、離線兩種樣式。線上樣式指在執行時執行,持久化類在載入時得到增強;離線樣式指在編譯後的步驟中進行增強。位元組碼插裝,指在“執行時”向 Java 類加入位元組碼。實際上不是在執行時,而是在 Java 類的“載入”過程中完成。

 

技術要點

 

  • 在 Maven `pom.xml` 中啟用 Hibernate 位元組碼插裝(像下麵這樣使用 Maven 位元組碼增強外掛)
  • 為需要延遲載入的列標記 `@Basic(fetch = FetchType.LAZY)`
  • 在 View 中禁用 Open Session

 

[示例程式碼][1]

 

[1]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootAttributeLazyLoadingBasic

 

2. 透過 Log4J 2 檢視系結引數

 

開發中,如果不能監測呼叫的SQL陳述句系結的引數,很有可能造成潛在的效能損失(例如 N+1 問題)。

 

> 譯註:“N+1 問題”即執行一次查詢 N 條主資料後,由於關聯引起的 N 次從資料查詢,因此會帶來了效能問題。一般來說,透過延遲載入可以部分緩解 N+1 帶來的效能問題。

 

“更新:”如果專案中”已經”配置了 Log4J 2,可以採用以下方案。如果沒有配置,建議使用 `TRACE`(感謝 Peter Wippermann 的建議)或 `log4jdbc`(感謝 Sergei Poznanski 的建議以及 [SO][2] 的答案)。這兩種方案不需要取消預設 Spring Boot 日誌功能。使用 `TRACE` 的例子參見[這裡][3],`log4jdbc` 的示例參見[這裡][4]。 

 

[2]:https://stackoverflow.com/questions/45346905/how-to-log-sql-queries-their-parameters-and-results-with-log4jdbc-in-spring-boo/45346996#45346996

[3]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootLogTraceViewBindingParameters

[4]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootLog4JdbcViewBindingParameters

 

“基於 Log4J 2 方案:”最好的辦法還是監視SQL陳述句系結的引數,可以透過 Log4J 2 logger 設定。

 

技術要點

 

  • 在 Maven `pom.xml` 中移除預設 Spring Boot 日誌依賴(參考上面的更新說明)
  • 在 Maven `pom.xml` 中加入 Log4j 2 依賴
  • 在 `log4j2.xml` 中新增以下配置:

 

```xml
"org.hibernate.type.descriptor.sql"

level=“trace”/>
“`

 

示例輸出

 

 

[示例程式碼][5]

 

[5]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootLog4j2ViewBindingParameters

 

3.如何透過 datasource-proxy 監視查詢細節

 

如果無法保證批處理正常工作,很容易會遇到嚴重的效能損失。即使已經配置了批處理並且認為會在後臺執行,還是有一些情況會造成批處理被禁用。為了確保這一點,可以使用 `hibernate.generate_statistics` 顯示詳細資訊(包括批處理細節),也可以使用 datasource-proxy。

 

“描述:”透過 [datasource-proxy][6] 檢視查詢細節(包括查詢型別、系結引數、批處理大小等)。

 

[6]:https://github.com/ttddyy/datasource-proxy

 

技術要點

 

  • 在 Maven `pom.xml` 中加入 `datasource-proxy` 依賴
  • 為 `DataSource` bean 建立 Post Processor 進行攔截
  • 用 `ProxyFactory` 和 `MethodInterceptor` 實現包裝 `DataSource` bean

 

示例輸出

 

 

[示例程式碼] [here][7]

 

[7]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDataSourceProxy

 

4. 透過 saveAll(Iterable entities) 在 MySQL(或其他 RDBMS)中執行批次插入

 

預設情況下,100次插入會生成100個 `INSERT` 陳述句,帶來100個資料庫行程開銷。

 

“描述:”批處理機制對 `INSERT`、`UPDATE` 和 `DELETE` 進行分組,能夠顯著降低資料庫行程數。批處理插入可以呼叫 `SimpleJpaRepository#saveAll(Iterable entities)` 方法,下麵是在 MySQL 中的應用步驟。

 

技術要點

 

  • 在 `application.properties` 中設定 `spring.jpa.properties.hibernate.jdbc.batch_size`
  • 在 `application.properties`中設定 `spring.jpa.properties.hibernate.generate_statistics`:檢查批處理是否正常工作
  • 在 `application.properties` JDBC URL 中設定 `rewriteBatchedStatements=true`:針對 MySQL 最佳化
  • 在 `application.properties` JDBC URL 中設定 `cachePrepStmts=true`:啟用快取。啟用 prepStmtCacheSize、prepStmtCacheSqlLimit 等引數前必須設定此引數
  • In `application.properties` JDBC URL 中設定 `useServerPrepStmts=true`:切換到服務端生成預處理陳述句,可能會帶來顯著效能提升
  • 在物體類中使用 [assigned generator][8]:MySQL `IDENTITY` 會禁用批處理
  • 在物體類中為 `Long` 屬性新增 `@Version` 註解:不僅可以避免批處理生成額外的 `SELECT`,還能減少多個請求事務中丟失 update。使用 `merge()` 替代 `persist()` 時會生成額外的 `SELECT`。`saveAll()` 實際呼叫 `save()`,如果物體物件ID非空會被看作已有物件。這時呼叫 `merge()` 觸發 Hibernate 生成 `SELECT` 檢查資料庫中是否存在相同標識
  • 註意:傳入 `saveAll()` 的物件數量不要“改寫“持久化背景關係。通常情況下,`EntityManager` 會定期執行 flush 和 clear,但是 `saveAll()` 執行過程中不會。因此,如果 `saveAll()` 傳入了大量資料,所有資料都會命中持久化背景關係(1級快取),並一直保持直到執行 flush 操作。這裡的配置適用於規模較小的資料,對於大資料的情況請參考例5

 

[8]:https://vladmihalcea.com/how-to-combine-the-hibernate-assigned-generator-with-a-sequence-or-an-identity-column/

 

示例輸出

 

 

[示例程式碼][9]

 

[9]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootBatchInsertsJpaRepository

 

5. 透過 EntityManager 在 MySQL(或其他 RDBMS)中執行批次插入

 

批處理可以提高效能,但是在執行 flush 前需要關註持久化背景關係中的資料量。在記憶體中儲存大量資料會導致效能下降,例4中的方法只適合資料量相對較少的情況。

 

“描述:”透過 `EntityManager` 在 MySQL(或其他 RDBMS)中執行批次插入。這種方法可以更好地控制持久化背景關係(1級快取) `flush()` 和 `clear()` 操作。Spring Boot 中 `saveAll(Iterableentities)` 做不到這點。其它好處,可以呼叫 `persist()` 而不是 `merge()` 方法,Spring Boot `saveAll(Iterable< S>entities)` 與 `save(S entity)` 預設呼叫前者。

 

技術要點

 

  • 在 `application.properties` 中設定 `spring.jpa.properties.hibernate.jdbc.batch_size`
  • 在 `application.properties` 中設定 `spring.jpa.properties.hibernate.generate_statistics`:檢查批處理是否正常工作
  • 在 `application.properties` JDBC URL 中設定  `rewriteBatchedStatements=true`:針對 MySQL 最佳化
  • 在 `application.properties` JDBC URL 中設定 `withcachePrepStmts=true`:啟用快取。啟用 prepStmtCacheSize、prepStmtCacheSqlLimit 等引數前必須設定此引數
  • 在 `application.properties` JDBC URL 中設定 `withuseServerPrepStmts=true`:切換到服務端生成預處理陳述句,可能會帶來顯著效能提升
  • 在物體類中使用 [assigned generator][8]:MySQL `IDENTITY` 會禁用批處理
  • 在 DAO 中定期對持久化背景關係執行 flush 和 clear,避免“改寫“持久化背景關係

 

示例輸出

 

 

[示例程式碼][10]

 

[10]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootBatchInsertsEntityManager

 

你可能也會對下麵內容感興趣

 

  • [6. 如何在 MySQL 中透過 JpaContext/EntityManager 執行批次插入][11]”
  • [7. 在 MySQL 中實現 Session 級批處理(Hibernate 5.2 或更高版本)][12]”

 

[11]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootBatchInsertsEntityManagerViaJpaContext

[12]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootBatchInsertsViaSession

 

8. 透過 Spring Data/EntityManager/Session 直接獲取結果

 

從資料庫獲取資料的方式決定了應用的執行效率,要最佳化查詢必須瞭解每種獲取資料方法的特點。在瞭解物體類’主鍵’的情況下,*直接獲取*是最簡單且實用的辦法。

 

“描述:”下麵是使用 Spring Data、`EntityManager` 和 Hibernate `Session` 直接獲取資料的示例:

 

技術要點

 

  • 透過 Spring Data 直接獲取資料,呼叫 `findById()`
  • 透過 `EntityManager#find()` 直接獲取資料
  • 透過 Hibernate `Session#get()` 直接獲取資料

 

[示例程式碼][13]

 

[13]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDirectFetching

 

9. 透過 Spring Data Projection 實現 DTO

 

獲取超出需要的資料是導致效能下降的常見問題之一。不僅如此,得到物體後不做修改也是一樣。

 

“描述:”透過 Spring Data Projection(DTO)從資料庫只獲取必須的資料。也可以檢視例子25至32。

 

技術要點

 

  • 編寫介面(projection),包含資料庫所需資料表指定列的 getter 方法
  • 編寫傳回 `List` 的查詢
  • 可能的話,要限制傳回的行數(例如,透過 `LIMIT`)。這個例子中,使用了 Spring Data repository 的內建 query builder 機制

 

示例輸出(選擇前2列,只獲取 “name” 和 “age”)

 

 

[示例程式碼][14]

 

[14]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaProjections

 

10. 如何在 MySQL 中儲存 UTC 時區

 

在資料庫中儲存不同格式或指定格式的日期、時間和時間戳會帶來日期轉換問題。

 

“描述:” 這個例子展示瞭如何在 MySQL 中以 UTC 時區儲存日期、時間和時間戳。對其他 RDBMS(例如 PostgreSQL),只要移除 `useLegacyDatetimeCode=false` 對應調整 JDBC URL 即可。

 

技術要點

 

  • `spring.jpa.properties.hibernate.jdbc.time_zone=UTC`
  • `spring.datasource.url=jdbc:mysql://localhost:3306/db_screenshot?useLegacyDatetimeCode=false`

 

[示例程式碼] [here][15]

 

> 譯註:執行時修改示例 url 為 jdbc:mysql://localhost:3306/db_screenshot?createDatabaseIfNotExist=true&useLegacyDatetimeCode;=false,設定引數 spring.jpa.hibernate.ddl-auto=create

 

[15]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootUTCTimezone

 

11. 透過 Proxy 得到父物體

 

執行的 SQL 越多,效能損失越大。盡可能減少執行的 SQL 數量非常重要,透過 Reference 是最易於使用的最佳化方法。

 

“描述:”`Proxy` 在子物體可以透過指向父物體的一個持久化取用表示時非常有用。這種情況下,執行`SELECT` 陳述句從資料庫獲得父物體會帶來效能損失且沒有意義。Hibernate 能夠對未初始化的 `Proxy` 設定基礎外來鍵值。

 

技術要點

 

  • 底層依賴 `EntityManager#getReference()`
  • 在 Spring 中呼叫 `JpaRepository#getOne()`
  • 在這個示例中,使用了 Hibernate `load()` 方法
  • 示例中有 `Tournament` 和 `TennisPlayer` 兩個實體,一個 tournament 包含多個 player(`@OneToMany`)
  • 透過 `Proxy` 獲取 tournament 物件(不會觸發 `SELECT`),接著建立一個 TennisPlayer 物件,把 `Proxy` 設為 player 的 tournament,最後儲存 player(觸發 `INSERT` 操作,在 tennis player 中插入 `tennis_player`)

 

示例輸出

 

命令列只輸出一條 `INSERT`,沒有 `SELECT` 陳述句。

 

[示例程式碼][16]

 

[16]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootPopulatingChildViaProxy

 

12. N+1 問題

 

“N+1問題”可能造成嚴重的效能損失。減少損失的首要任務是定位問題。

 

N+1 本質上是一個延遲載入問題(預先載入也不例外)。缺乏對實際執行SQL進行監測,很可能會造成 N+1 問題,最好的解決辦法是 JOIN+DTO(例36至例42)。

 

技術要點

 

  • 定義 `Category` 和 `Product` 兩類物體,關係為 `@OneToMany`
  • 延遲載入 `Product`,不主動載入 `Category`(只生成1條查詢)
  • 迴圈讀取 `Product` 集合, 對每個產品獲取 `Category`(生成N條查詢)

 

示例輸出

[示例程式碼][17]

 

[17]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootSimulateNPlus1

 

13. 透過 HINT_PASS_DISTINCT_THROUGH 最佳化 Distinct SELECT

 

把 `SELECT DISTINCT` 傳遞給 RDBMS 會[影響效能][18]。

 

[18]:http://in.relation.to/2016/08/04/introducing-distinct-pass-through-query-hint/

 

“描述:” Hibernate 5.2.2 開始,可以透過 `HINT_PASS_DISTINCT_THROUGH` 最佳化 `SELECT DISTINCT`。不會把 `DISTINCT` 關鍵字傳給 RDBMS,而是由 Hibernate 刪除重覆資料。

 

技術要點

 

  • 使用 `@QueryHints(value = @QueryHint(name = HINT_PASS_DISTINCT_THROUGH, value = “false”))`

 

示例輸出

 

 

[示例程式碼][19]

 

[19]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootHintPassDistinctThrough

 

14. 啟用臟資料跟蹤

 

Java 反射執行速度慢,通常被看作效能損失。

 

“描述:”Hibernate 5 之前,臟資料檢查機制基於 Java Reflection API。自 Hibernate 5 開始,轉而採用了**位元組碼增強**技術。後者的效能更好,物體數量較多時效果尤其明顯。

 

技術要點

 

  • 在 `pom.xml` 中增加外掛配置(例如,使用 Maven bytecode enhancement 外掛)

 

示例輸出

 

  • 位元組碼增強效果可以在 `User.class` 上[看到][20]

 

[示例程式碼][21]

 

[20]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/blob/master/HibernateSpringBootEnableDirtyTracking/Bytecode%20Enhancement%20User.class/User.java

[21]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootEnableDirtyTracking

 

15. 在物體和查詢上使用 Java 8 Optional

 

把 Java 8 `Optional` 作為處理 `null` 的“銀彈”可能弊大於利,最好的方式還是按照設計的意圖使用。

 

“描述:”下麵的示例展示瞭如何在物體和查詢中正確使用 Java 8 `Optional`。

 

技術要點

 

  • 使用 Spring Data 內建查詢方法傳回 `Optional`(例如 `findById()`)
  • 自己編寫查詢方法傳回 `Optional`
  • 在物體 getter 方法中使用 `Optional`
  • 可以使用 `data-mysql.sql` 指令碼驗證不同場景

 

[示例程式碼][22]

 

[22]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootEnableDirtyTracking

 

16. 如何正確建立 @OneToMany 雙向關係

 

實現 `@OneToMany` 雙向關係有幾個陷阱,相信你也希望一開始就能實現正確。

 

“描述:”下麵的示例應用展示瞭如何正確實現 `@OneToMany` 雙向關聯。

 

技術要點

 

  • “總是”建立父子級聯
  • 對父親標記 `mappedBy`
  • 對父親使用 `orphanRemoval`,移除沒有取用的子物件
  • 在父節點上使用 helper 方法實現關聯同步
  • “總是”使用延遲載入
  • 使用業務主鍵或物體識別符號,參考[這篇介紹][23]覆寫 `equals()` 和 `hashCode()` 方法。

 

[示例程式碼][24]

 

[23]:https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/

[24]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootOneToManyBidirectional

 

17. JPQL/HQL 查詢資料

 

在不具備直接查詢的情況下,可以考慮透過 JPQL/HQL 查詢資料。

 

“描述:”下麵的示例展示瞭如何透過 `JpaRepository`、`EntityManager` 和 `Session` 進行查詢。

 

技術要點

 

  • 對 `JpaRepository` 使用 `@Query` 註解或者建立 Spring Data Query
  • 對 `EntityManager` 與 `Session` 使用 `createQuery()` 方法

 

[示例程式碼][25]

 

[25]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootQueryFetching

 

18. 避免在 MySQL 與 Hibernate 5 中使用 AUTO Generator 型別

 

在 MySQL 開發過程中,儘量避免使用 `TABLE` 生成器,最好[永遠不要使用][26]。

 

[26]:https://vladmihalcea.com/why-you-should-never-use-the-table-identifier-generator-with-jpa-and-hibernate/

 

“描述:” 在使用 MySQL 和 Hibernate 5 開發時,`GenerationType.AUTO` 型別的生成器會呼叫 `TABLE` 生成器,造成嚴重的效能損失。可以透過 `GenerationType.IDENTITY` 呼叫 `IDENTITY` 生成器或者使用 *native* 生成器。

 

技術要點

 

  • 使用 `GenerationType.IDENTITY` 取代 `GenerationType.AUTO`
  • 使用[示例程式碼][27],呼叫  *native* 生成器

 

[27]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootAutoGeneratorType

 

示例輸出

 

 

[示例程式碼][28]

 

[28]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootAutoGeneratorType

 

19. 多餘的 save() 呼叫

 

大家都喜歡使用 `save()`。由於 Hibernate 採用了臟資料檢查機制避免多餘呼叫,`save()` 對於託管物體並不適用。

 

“描述:” 下麵的示例展示了對於託管物體呼叫 `save()` 方法是多餘的。

 

技術要點

 

  • Hibernate 會為每個託管物體呼叫 `UPDATE` 陳述句,不需要顯示呼叫 `save()` 方法
  • 多餘的呼叫意味著效能損失(參見[這篇文章][29])

 

[示例程式碼][30]

 

[29]https://vladmihalcea.com/jpa-persist-and-merge/

[30]https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootRedundantSave

 

20. PostgreSQL (BIG)SERIAL 與批次插入

 

在 PostgreSQL 中,使用 `GenerationType.IDENTITY` 會禁用批次插入。

 

“描述:” `(BIG)SERIAL` 與 MySQL 的 `AUTO_INCREMENT` 功能“接近”。在這個示例中,我們透過 `GenerationType.SEQUENCE` 開啟批次插入,同時透過 `hi/lo` 演演算法進行了最佳化。

 

技術要點

 

  • 使用 `GenerationType.SEQUENCE` 取代 `GenerationType.IDENTITY`
  • 透過 `hi/lo` 演演算法在一個資料庫行程中完成多個識別符號讀取(還可以使用 Hibernate `pooled` 和 `pooled-lo` 識別符號生成器,它們是 `hi/lo` 的改進版)

 

示例輸出

 

 

[示例程式碼][31]

 

[31]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootBatchingAndSerial

 

> 譯註:示例中 `createDatabaseIfNotExist=true` 引數對 PostgreSQL 無效,需要手動建立 `db_users` 資料庫。

 

21. JPA 繼承之 Single Table

 

JPA 支援 `SINGLE_TABLE`、`JOINED` 和 `TABLE_PER_CLASS` 繼承策略,有著各自優缺點。以 `SINGLE_TABLE` 為例,讀寫速度快但不支援對子類中的列設定 `NOT NULL`。

 

“描述:”下麵的示例展示了 JPA Single Table 繼承策略(`SINGLE_TABLE`)。

 

技術要點

 

  • 這是 JPA 預設的繼承策略(`@Inheritance(strategy=InheritanceType.SINGLE_TABLE)`)
  • 所有繼承結構中的類都會被對映到資料庫中的單個表

 

示例輸出(下麵是四個物體得到的單個表)

 

 

[示例程式碼][32]

 

[32]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootSingleTableInheritance

 

22. 如何對 SQL 陳述句統計和斷言

 

如果不對 SQL 陳述句進行統計和斷言,很容易對後臺執行的 SQL 陳述句失去控制,進而造成效能損失。 

 

“描述:”下麵的示例展示瞭如何對後臺 SQL 陳述句進行統計和斷言。統計 SQL 非常有用,能夠確保不會生成多餘的 SQL(例如,可以對預期的陳述句數量斷言檢測 N+1 問題)。

 

技術要點

 

  • 在 Maven `pom.xml` 中新增 `datasource-proxy` 依賴和 Vlad Mihalcea 的 `db-util`
  • 新建 `ProxyDataSourceBuilderwithcountQuery()`
  • `SQLStatementCountValidator.reset()` 重置計數
  • 透過 `assertInsert{Update/Delete/Select}Count(long expectedNumberOfSql` 對 `INSERT`、`UPDATE`、`DELETE` 和 `SELECT` 進行斷言

 

示例輸出(期望的 SQL 陳述句數量與實際生成的數量不一致時丟擲異常)

 

[示例程式碼][33]

 

[33]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootCountSQLStatements

 

23. 如何使用 JPA 回呼

 

為物體系結事件處理時,記得使用 JPA 內建回呼,不要重新發明輪子。

 

“描述:”下麵的示例展示瞭如何啟用 JPA 回呼(`Pre/PostPersist`、`Pre/PostUpdate`、`Pre/PostRemove` 和 `PostLoad`)。

 

技術要點

 

  • 在物體中編寫回呼方法並挑選合適的註解
  • Bean Class 中帶註解的回呼方法傳回型別必須為 `void` 且不帶引數

 

示例輸出

 

[示例程式碼][34]

 

[34]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootJpaCallbacks

 

24. @OneToOne 與 @MapsId

 

雙向 `@OneToOne` 效率不及單向 `@OneToOne`,後者與父表共享主鍵。

 

“描述:” 下麵的示例展示了為何建議使用 `@OneToOne` 和 `@MapsId` 取代 `@OneToOne`。

 

技術要點

 

  • 在子物體上使用 `@MapsId`
  • 對於 `@OneToOne` 關聯,基本上會與父表共享主鍵。

 

[示例程式碼][35]

 

[35]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootOneToOneMapsId

 

25. 透過 SqlResultSetMapping 設定 DTO

 

超出需要獲取資料是不好的習慣。另一種常見的錯誤,沒有打算修改物體物件卻獲取並儲存到持久化背景關係中,同樣會導致效能問題。例25至例32展示瞭如何使用不同方法提取 DTO。

 

“描述:“下麵的示例展示瞭如何透過 `SqlResultSetMapping` 和 `EntityManager` 使用 DTO 提取需要的資料。

 

技術要點

 

  • 使用 `SqlResultSetMapping` 和 `EntityManager`
  • 使用 Spring Data Projection 時,請檢查例9中的註意事項

 

[示例程式碼][36]

 

[36]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoSqlResultSetMapping

贊(0)

分享創造快樂