hibernate语句_如何优化Hibernate EllementCollection语句
hibernate語句
介紹
Hibernate支持三種數據映射類型 : 基本 (例如String,int), Embeddable和Entity 。 通常,數據庫行被映射到Entity ,每個數據庫列都與一個基本屬性相關聯。 當將多個字段映射組合到一個可重用的組中時, 可嵌入的類型更為常見( Embeddable被合并到擁有的實體映射結構中)。
這兩種基本類型和Embeddables可以通過被關聯到一個實體 @ElementCollection ,在一實體-許多類非實體關系。
測試時間
對于即將到來的測試用例,我們將使用以下實體模型:
修補程序具有變更可嵌入對象的集合。
@ElementCollection @CollectionTable(name="patch_change",joinColumns=@JoinColumn(name="patch_id") ) private List<Change> changes = new ArrayList<>();Change對象建模為Embeddable類型,并且只能通過其所有者Entity進行訪問。 Embeddable沒有標識符 ,因此無法通過JPQL查詢。 Embeddable生命周期綁定到其所有者的生命周期,因此任何實體狀態轉換都會自動傳播到Embeddable集合。
首先,我們需要添加一些測試數據:
doInTransaction(session -> {Patch patch = new Patch();patch.getChanges().add(new Change("README.txt", "0a1,5..."));patch.getChanges().add(new Change("web.xml", "17c17..."));session.persist(patch); });添加一個新元素
讓我們看看將新的Change添加到現有Patch時會發生什么:
doInTransaction(session -> {Patch patch = (Patch) session.get(Patch.class, 1L);patch.getChanges().add(new Change("web.xml", "1d17...")); });此測試生成以下SQL輸出:
DELETE FROM patch_change WHERE patch_id = 1INSERT INTO patch_change (patch_id, diff, path) VALUES (1, '0a1,5...', 'README.txt') INSERT INTO patch_change(patch_id, diff, path) VALUES (1, '17c17...', 'web.xml') INSERT INTO patch_change(patch_id, diff, path) VALUES (1, '1d17...', 'web.xml')默認情況下,任何收集操作最終都會重新創建整個數據集。 這種行為僅對于內存中的集合是可接受的,并且從數據庫的角度來看是不合適的。 數據庫必須刪除所有現有的行,而只是重新添加它們的后綴。 我們在該表上擁有的索引越多,性能損失就越大。
刪除元素
刪除元素沒有什么不同:
doInTransaction(session -> {Patch patch = (Patch) session.get(Patch.class, 1L);patch.getChanges().remove(0); });此測試用例生成以下SQL語句:
DELETE FROM patch_change WHERE patch_id = 1INSERT INTO patch_change(patch_id, diff, path) VALUES (1, '17c17...', 'web.xml')刪除了所有表行,并將剩余的內存中條目刷新到數據庫中。
Java Persistence Wiki Book清楚地記錄了這種行為:
JPA 2.0規范沒有提供在Embeddable中定義ID的方法。 但是,要刪除或更新ElementCollection映射的元素,通常需要一些唯一鍵。 否則,在每次更新時,JPA提供程序都將需要從實體的CollectionTable中刪除所有內容,然后再將值插入回去。 因此,JPA提供程序很可能會假定Embeddable中所有字段的組合與外鍵(JoinColumn(s))組合在一起都是唯一的。 但是,如果Embeddable很大或很復雜,這可能效率很低,或者根本不可行。
一些JPA提供程序可能允許在可嵌入對象中指定ID,以解決此問題。 請注意,在這種情況下,對于集合,該ID僅需是唯一的,而不是該表,因為其中包括外鍵。 有些可能還允許將CollectionTable上的唯一選項用于此目的。 否則,如果您的Embeddable很復雜,則可以考慮將其設為實體,而改用OneToMany。
添加一個OrderColumn
為了優化ElementCollection行為,我們需要應用適用于一對多關聯的相同技術。 元素的收集就像是單向的一對多關系,并且我們已經知道idbag的 性能比單向bag更好 。
因為可嵌入對象不能包含標識符,所以我們至少可以添加一個訂單列,以便可以唯一地標識每一行。 讓我們看看將@OrderColumn添加到元素集合時會發生什么:
@ElementCollection @CollectionTable(name="patch_change",joinColumns=@JoinColumn(name="patch_id") ) @OrderColumn(name = "index_id") private List<Change> changes = new ArrayList<>();刪除實體后,以前的測試結果沒有任何改善:
DELETE FROM patch_change WHERE patch_id = 1INSERT INTO patch_change(patch_id, diff, path) VALUES (1, '17c17...', 'web.xml')這是因為在阻止重新創建集合時, AbstractPersistentCollection將檢查可為空的列:
@Override public boolean needsRecreate(CollectionPersister persister) {if (persister.getElementType() instanceof ComponentType) {ComponentType componentType = (ComponentType) persister.getElementType();return !componentType.hasNotNullProperty();}return false; }現在,我們將添加NOT NULL約束并重新運行測試:
@Column(name = "path", nullable = false) private String path;@Column(name = "diff", nullable = false) private String diff;添加一個新的有序元素
將元素添加到列表的末尾將生成以下語句:
INSERT INTO patch_change(patch_id, index_id, diff, path) VALUES (1, 2, '1d17...', 'web.xml')index_id列用于持久存儲內存中的收集順序。 添加到集合的末尾不會影響現有元素的順序,因此僅需要一個INSERT語句。
添加一個新的第一個元素
如果我們在列表的開頭添加一個新元素:
doInTransaction(session -> {Patch patch = (Patch) session.get(Patch.class, 1L);patch.getChanges().add(0, new Change("web.xml", "1d17...")); });生成以下SQL輸出:
UPDATE patch_change SET diff = '1d17...',path = 'web.xml' WHERE patch_id = 1AND index_id = 0 UPDATE patch_change SET diff = '0a1,5...',path = 'README.txt' WHERE patch_id = 1AND index_id = 1INSERT INTO patch_change (patch_id, index_id, diff, path) VALUES (1, 2, '17c17...', 'web.xml')現有數據庫條目已更新,以反映新的內存中數據結構。 由于新添加的元素已添加到列表的開頭,因此它將觸發對表的第一行的更新。 所有INSERT語句在列表的末尾發出,并且所有現有元素均根據新的列表順序進行更新。
@OrderColumn Java持久性文檔中對此行為進行了說明:
當更新關聯或元素集合時,持久性提供程序維護order列的值的連續(非稀疏)排序。 第一個元素的訂單列值為0。
刪除有序元素
如果我們刪除最后一個條目:
doInTransaction(session -> {Patch patch = (Patch) session.get(Patch.class, 1L);patch.getChanges().remove(patch.getChanges().size() - 1); });僅發出一個DELETE語句:
DELETE FROM patch_change WHERE patch_id = 1AND index_id = 1刪除第一個元素條目
如果刪除第一個元素,則會執行以下語句:
DELETE FROM patch_change WHERE patch_id = 1AND index_id = 1 UPDATE patch_change SET diff = '17c17...',path = 'web.xml' WHERE patch_id = 1AND index_id = 0Hibernate刪除所有多余的行,然后更新其余的行。
從中間刪除
如果我們從列表中間刪除一個元素:
doInTransaction(session -> {Patch patch = (Patch) session.get(Patch.class, 1L);patch.getChanges().add(new Change("web.xml", "1d17..."));patch.getChanges().add(new Change("server.xml", "3a5...")); });doInTransaction(session -> {Patch patch = (Patch) session.get(Patch.class, 1L);patch.getChanges().remove(1); });執行以下語句:
DELETE FROM patch_change WHERE patch_id = 1AND index_id = 3UPDATE patch_change SET diff = '1d17...',path = 'web.xml' WHERE patch_id = 1AND index_id = 1 UPDATE patch_change SET diff = '3a5...',path = 'server.xml' WHERE patch_id = 1AND index_id = 2有序ElementCollection的更新如下:
- 調整數據庫表的大小,使用DELETE語句刪除表末尾的多余行。 如果內存中的集合大于數據庫中的集合,則所有INSERT語句將在列表的末尾執行
- 添加/刪除條目之前的所有元素均保持不變
- 添加/刪除元素之后的其余元素將更新以匹配新的內存中收集狀態
結論
與一對多 反向關聯相比, ElementCollection更難優化。 如果集合經常更新,則元素集合最好用一對多關聯替換。 當我們不想為表示外鍵端而添加額外的實體時,元素集合更適合于很少更改的數據。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/05/how-to-optimize-hibernate-ellementcollection-statements.html
hibernate語句
總結
以上是生活随笔為你收集整理的hibernate语句_如何优化Hibernate EllementCollection语句的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浏览器总是跳转到缓存界面_跳转到企业缓存
- 下一篇: 佐良娜的训练安卓破解版下载(佐良娜的训练