使用JPA和Hibernate延遲載入實體屬性的最佳方法 - Vlad Mihalcea
獲取實體時,也會載入所有屬性。這是因為每個隱式使用@Basic實體屬性提取策略都預設FetchType.EAGER。
但是,屬性獲取策略可以設定為FetchType.LAZY,在這種情況下,實體屬性只有在第一次訪問時才載入,透過select語言的執行。
@Basic(fetch = FetchType.LAZY)
僅此配置是不夠的,因為Hibernate需要位元組碼檢測來攔截屬性訪問請求並按需發出select語句。
使用Maven位元組碼增強外掛時,enableLazyInitialization必須將配置屬性設定true為以下示例中所示:
<plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin</artifactId> <version>${hibernate.version}</version> <executions> <execution> <configuration> <failOnError>true</failOnError> <enableLazyInitialization>true</enableLazyInitialization> </configuration> <goals> <goal>enhance</goal> </goals> </execution> </executions> </plugin> |
有了這個配置,所有JPA實體類都將使用延遲屬性獲取進行檢測。此過程發生在構建時,就在從關聯的原始檔編譯實體類之後。
與儲存大量資料的列型別處理當屬性延遲抓取機構是非常有用的(例如BLOB,CLOB,VARBINARY)。這樣,可以在不自動從基礎大型列型別中載入資料的情況下獲取實體,從而提高效能。
演示
為了演示屬性延遲提取的工作原理,以下示例將使用Attachment可以儲存任何媒體型別的實體(例如PNG,PDF,MPEG)。
@Entity @Table(name = "attachment") public class Attachment { @Id @GeneratedValue private Long id; private String name; @Enumerated @Column(name = "media_type") private MediaType mediaType; @Lob @Basic(fetch = FetchType.LAZY) private byte[] content; //Getters and setters omitted for brevity } |
在每個實體負載上都要急切地獲取諸如實體識別符號,名稱或媒體型別之類的屬性。另一方面,只有在被應用程式程式碼訪問時,才應該懶惰地獲取媒體檔案內容。
在檢測Attachment實體後,類位元組碼更改如下:
@Transient private transient PersistentAttributeInterceptor $$_hibernate_attributeInterceptor; public byte[] getContent() { return $$_hibernate_read_content(); } public byte[] $$_hibernate_read_content() { if ($$_hibernate_attributeInterceptor != null) { this.content = ((byte[]) $$_hibernate_attributeInterceptor.readObject( this, "content", this.content)); } return this.content; } |
執行以下測試用例時:
Attachment book = entityManager.find( Attachment.class, bookId); LOGGER.debug("Fetched book: {}", book.getName()); assertArrayEquals( Files.readAllBytes(bookFilePath), book.getContent() ); |
Hibernate生成以下SQL查詢:
SELECT a.id AS id1_0_0_, a.media_type AS media_ty3_0_0_, a.name AS name4_0_0_ FROM attachment a WHERE a.id = 1 -- Fetched book: High-Performance Java Persistence SELECT a.content AS content2_0_ FROM attachment a WHERE a.id = 1 |
因為它標記有FetchType.LAZY註釋並且啟用了延遲提取位元組碼增強,所以content不會提取該列以及初始化Attachment實體的所有其他列。只有當資料訪問層嘗試訪問該content屬性時,Hibernate才會發出輔助選擇以載入此屬性。
就像FetchType.LAZY關聯一樣,這種技術很容易出現N + 1個查詢問題,因此建議謹慎行事。位元組碼增強機制的一個細微缺點是所有實體屬性,而不僅僅是標記有FetchType.LAZY註釋的屬性,將被轉換,如前所述。
獲取子實體
另一種避免載入相當大的表列的方法是將多個子實體對映到同一個資料庫表。
比如BaseAttachment有兩個子類Attachment實體和AttachmentSummary。
無論是Attachment實體和AttachmentSummary子實體繼承BaseAttachment所有公共屬性
@MappedSuperclass public class BaseAttachment { @Id @GeneratedValue private Long id; private String name; @Enumerated @Column(name = "media_type") private MediaType mediaType; //Getters and setters omitted for brevity } |
雖然AttachmentSummary擴充套件BaseAttachment而沒有宣告任何新屬性:
@Entity @Table(name = "attachment") public class AttachmentSummary extends BaseAttachment {} |
Attachment實體繼承超類BaseAttachment所有基本屬性並對映content列。
@Entity @Table(name = "attachment") public class Attachment extends BaseAttachment { @Lob private byte[] content; //Getters and setters omitted for brevity } |
當抓取AttachmentSummary子實體時:
AttachmentSummary bookSummary = entityManager.find( AttachmentSummary.class, bookId); |
產生SQL:
SELECT a.id as id1_0_0_, a.media_type as media_ty2_0_0_, a.name as name3_0_0_ FROM attachment a WHERE a.id = 1 |
當抓取Attachment 實體時:
Attachment book = entityManager.find( Attachment.class, bookId); |
Hibernate將從底層資料庫表中獲取所有列:
SELECT a.id as id1_0_0_, a.media_type as media_ty2_0_0_, a.name as name3_0_0_, a.content as content4_0_0_ FROM attachment a WHERE a.id = 1 |
結論
對於延遲獲取實體屬性,您可以使用位元組碼增強或子實體兩種方式。
雖然位元組碼檢測允許您每個表只使用一個實體,但子實體更靈活,甚至可以提供更好的效能,因為它們在讀取實體屬性時不涉及攔截器呼叫。
子實體方式其實就是將由大資料如圖片或檔案的物件和文字小資料的物件分開。
在讀取資料時,子實體與DTO投影非常相似。但是,與DTO投影不同,子實體可以跟蹤狀態更改並將它們傳播到資料庫。
相關文章
- 使用JPA和Hibernate呼叫儲存過程的最佳方法 - Vlad Mihalcea儲存過程
- JavaScript 中的延遲載入屬性模式JavaScript模式
- Hibernate 延遲載入原理
- 批處理最佳實踐 - Vlad Mihalcea
- hibernate延遲載入(get和load的區別)
- 使用 Spring Transactional 註釋的最佳方式 - Vlad MihalceaSpring
- 在JPA中請優先使用sequence策略生成實體識別符號的值 - Vlad Mihalcea符號
- mybatis延遲載入和快取MyBatis快取
- 如何啟用Hibernate慢查詢日誌? -Vlad Mihalcea
- javascript實現延遲載入效果JavaScript
- JavaScript實現圖片的延遲載入JavaScript
- 前端效能優化——延遲載入和非同步載入前端優化非同步
- Mybatis延遲載入、快取MyBatis快取
- 延遲載入 Dex 檔案
- 關於延遲載入,立即載入的問題
- SQL 搜尋方法或鍵集分頁 - Vlad MihalceaSQL
- EF中延遲載入的那些事
- 影像延遲載入 && 列表圖順序載入
- 延遲載入的一些知識和誤區
- 如何在Hibernate/JPA的實體和查詢中使用Java 8 Optional?Java
- 圖片延遲載入策略(JavaScript)JavaScript
- Spring Boot 2.2 中的延遲載入Spring Boot
- 適合用於資料庫主鍵的最佳UUID工具庫 - Vlad Mihalcea資料庫UI
- mybatis入門基礎(七)----延遲載入MyBatis
- Hibernate annotation, JPA如何對映多個屬性為unique
- 在AngularJS中實現一個延遲載入的DirectiveAngularJS
- 【漸進】延遲載入機制的簡易實現(上)
- vue計算屬性和vue實力的屬性和方法Vue
- VIM Lazy Load 懶載入/延遲載入技術
- 延遲載入演算法微服務的模型演算法微服務模型
- 專案分享六:圖片的延遲載入
- 一次系統延遲性最佳化案例
- Entity Framework 實體載入外來鍵屬性的問題Framework
- 實現簡單延遲佇列和分散式延遲佇列佇列分散式
- Angular2+,路由預載入--預先載入延遲模組Angular路由
- [譯] 延遲載入 React Components (用 react.lazy 和 suspense)React
- [譯] 網速敏感的視訊延遲載入方案
- 實驗6 方法和屬性