日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

休眠事实:多级访存

發(fā)布時間:2023/12/3 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 休眠事实:多级访存 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在多個級別上檢索根實(shí)體及其子關(guān)聯(lián)是很常見的。

在我們的示例中,我們需要加載一個包含其樹,分支和葉子的森林,并且我們將嘗試查看Hibernate對于三種集合類型的行為:集合,索引列表和包。

這是我們的類層次結(jié)構(gòu)的樣子:

使用集和索引列表很簡單,因為我們可以通過運(yùn)行以下JPA-QL查詢來加載所有實(shí)體:

Forest f = entityManager.createQuery( "select f " + "from Forest f " + "join fetch f.trees t " + "join fetch t.branches b " + "join fetch b.leaves l ", Forest.class) .getSingleResult();

執(zhí)行的SQL查詢?yōu)?#xff1a;

SELECT forest0_.id AS id1_7_0_,trees1_.id AS id1_18_1_,branches2_.id AS id1_4_2_,leaves3_.id AS id1_10_3_,trees1_.forest_fk AS forest_f3_18_1_,trees1_.index AS index2_18_1_,trees1_.forest_fk AS forest_f3_7_0__,trees1_.id AS id1_18_0__,trees1_.index AS index2_0__,branches2_.index AS index2_4_2_,branches2_.tree_fk AS tree_fk3_4_2_,branches2_.tree_fk AS tree_fk3_18_1__,branches2_.id AS id1_4_1__,branches2_.index AS index2_1__,leaves3_.branch_fk AS branch_f3_10_3_,leaves3_.index AS index2_10_3_,leaves3_.branch_fk AS branch_f3_4_2__,leaves3_.id AS id1_10_2__,leaves3_.index AS index2_2__ FROM forest forest0_ INNER JOIN tree trees1_ ON forest0_.id = trees1_.forest_fk INNER JOIN branch branches2_ ON trees1_.id = branches2_.tree_fk INNER JOIN leaf leaves3_ ON branches2_.id = leaves3_.branch_fk

但是,當(dāng)我們的子級關(guān)聯(lián)映射為Bags時,相同的JPS-QL查詢將引發(fā)“ org.hibernate.loader.MultipleBagFetchException”。

萬一您不能更改映射(用集合或索引列表替換包),您可能會想嘗試以下方法:

BagForest forest = entityManager.find(BagForest.class, forestId); for (BagTree tree : forest.getTrees()) {for (BagBranch branch : tree.getBranches()) {branch.getLeaves().size(); } }

但這會產(chǎn)生大量的SQL查詢,效率很低:

select trees0_.forest_id as forest_i3_1_1_, trees0_.id as id1_3_1_, trees0_.id as id1_3_0_, trees0_.forest_id as forest_i3_3_0_, trees0_.index as index2_3_0_ from BagTree trees0_ where trees0_.forest_id=? select branches0_.tree_id as tree_id3_3_1_, branches0_.id as id1_0_1_, branches0_.id as id1_0_0_, branches0_.index as index2_0_0_, branches0_.tree_id as tree_id3_0_0_ from BagBranch branches0_ where branches0_.tree_id=? select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id=? select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id=? select branches0_.tree_id as tree_id3_3_1_, branches0_.id as id1_0_1_, branches0_.id as id1_0_0_, branches0_.index as index2_0_0_, branches0_.tree_id as tree_id3_0_0_ from BagBranch branches0_ where branches0_.tree_id=? select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id=? select leaves0_.branch_id as branch_i3_0_1_, leaves0_.id as id1_2_1_, leaves0_.id as id1_2_0_, leaves0_.branch_id as branch_i3_2_0_, leaves0_.index as index2_2_0_ from BagLeaf leaves0_ where leaves0_.branch_id=?

因此,我的解決方案是簡單地獲取最低級別的子級,并在實(shí)體層次結(jié)構(gòu)中一直獲取所有需要的關(guān)聯(lián)。

運(yùn)行此代碼:

List<BagLeaf> leaves = transactionTemplate.execute(new TransactionCallback<List<BagLeaf>>() {@Overridepublic List<BagLeaf> doInTransaction(TransactionStatus transactionStatus) {List<BagLeaf> leaves = entityManager.createQuery("select l " +"from BagLeaf l " +"inner join fetch l.branch b " +"inner join fetch b.tree t " +"inner join fetch t.forest f " +"where f.id = :forestId",BagLeaf.class).setParameter("forestId", forestId).getResultList();return leaves;} });

僅生成一個SQL查詢:

SELECT bagleaf0_.id AS id1_2_0_,bagbranch1_.id AS id1_0_1_,bagtree2_.id AS id1_3_2_,bagforest3_.id AS id1_1_3_,bagleaf0_.branch_id AS branch_i3_2_0_,bagleaf0_.index AS index2_2_0_,bagbranch1_.index AS index2_0_1_,bagbranch1_.tree_id AS tree_id3_0_1_,bagtree2_.forest_id AS forest_i3_3_2_,bagtree2_.index AS index2_3_2_ FROM bagleaf bagleaf0_INNER JOIN bagbranch bagbranch1_ON bagleaf0_.branch_id = bagbranch1_.idINNER JOIN bagtree bagtree2_ON bagbranch1_.tree_id = bagtree2_.idINNER JOIN bagforest bagforest3_ON bagtree2_.forest_id = bagforest3_.id WHERE bagforest3_.id = ?

我們得到了一個葉子對象的列表,但是每個葉子還獲取了分支,后者獲取了樹,然后獲取了森林。 不幸的是,Hibernate無法從這樣的查詢結(jié)果中神奇地創(chuàng)建上下層次結(jié)構(gòu)。

嘗試通過以下方式進(jìn)入袋子:

leaves.get(0).getBranch().getTree().getForest().getTrees();

只是拋出LazyInitializationException,因為我們試圖在打開的持久性上下文之外訪問未初始化的惰性代理列表。

因此,我們只需要從Leaf對象的List自己重新創(chuàng)建Forest層次結(jié)構(gòu)。

這就是我的方法:

EntityGraphBuilder entityGraphBuilder = new EntityGraphBuilder(new EntityVisitor[] {BagLeaf.ENTITY_VISITOR, BagBranch.ENTITY_VISITOR, BagTree.ENTITY_VISITOR, BagForest.ENTITY_VISITOR }).build(leaves); ClassId<BagForest> forestClassId = new ClassId<BagForest>(BagForest.class, forestId); BagForest forest = entityGraphBuilder.getEntityContext().getObject(forestClassId);

EntityGraphBuilder是我編寫的一個實(shí)用程序,它接受EntityVisitor對象的數(shù)組并將其應(yīng)用于訪問的對象。 遞歸處理到Forest對象,我們用新的Hibernate集合替換了Hibernate集合,將每個子代添加到父子代集合中。

由于替換了子級集合,因此不安全地在新的Persistence Context中重新附加/合并此對象是比較安全的,因為所有Bags都將標(biāo)記為臟。

這是實(shí)體使用其訪問者的方式:

private <T extends Identifiable, P extends Identifiable> void visit(T object) {Class<T> clazz = (Class<T>) object.getClass();EntityVisitor<T, P> entityVisitor = visitorsMap.get(clazz);if (entityVisitor == null) {throw new IllegalArgumentException("Class " + clazz + " has no entityVisitor!");}entityVisitor.visit(object, entityContext);P parent = entityVisitor.getParent(object);if (parent != null) {visit(parent);} }

基本的EntityVisitor看起來像這樣:

public void visit(T object, EntityContext entityContext) {Class<T> clazz = (Class<T>) object.getClass();ClassId<T> objectClassId = new ClassId<T>(clazz, object.getId());boolean objectVisited = entityContext.isVisited(objectClassId);if (!objectVisited) {entityContext.visit(objectClassId, object);}P parent = getParent(object);if (parent != null) {Class<P> parentClass = (Class<P>) parent.getClass();ClassId<P> parentClassId = new ClassId<P>(parentClass, parent.getId());if (!entityContext.isVisited(parentClassId)) {setChildren(parent);}List<T> children = getChildren(parent);if (!objectVisited) {children.add(object);}} }

此代碼打包為實(shí)用程序,并且通過擴(kuò)展EntityVisitors來進(jìn)行自定義,如下所示:

public static EntityVisitor<BagForest, Identifiable> ENTITY_VISITOR = new EntityVisitor<BagForest, Identifiable>(BagForest.class) {};public static EntityVisitor<BagTree, BagForest> ENTITY_VISITOR = new EntityVisitor<BagTree, BagForest>(BagTree.class) {public BagForest getParent(BagTree visitingObject) {return visitingObject.getForest();}public List<BagTree> getChildren(BagForest parent) {return parent.getTrees();}public void setChildren(BagForest parent) {parent.setTrees(new ArrayList<BagTree>());} };public static EntityVisitor<BagBranch, BagTree> ENTITY_VISITOR = new EntityVisitor<BagBranch, BagTree>(BagBranch.class) {public BagTree getParent(BagBranch visitingObject) {return visitingObject.getTree();}public List<BagBranch> getChildren(BagTree parent) {return parent.getBranches();}public void setChildren(BagTree parent) {parent.setBranches(new ArrayList<BagBranch>());} };public static EntityVisitor<BagLeaf, BagBranch> ENTITY_VISITOR = new EntityVisitor<BagLeaf, BagBranch>(BagLeaf.class) {public BagBranch getParent(BagLeaf visitingObject) {return visitingObject.getBranch();}public List<BagLeaf> getChildren(BagBranch parent) {return parent.getLeaves();}public void setChildren(BagBranch parent) {parent.setLeaves(new ArrayList<BagLeaf>());} };

這不是“本身”的訪客模式,但與它有點(diǎn)類似。 盡管只使用索引列表或集合總會更好,但是您仍然可以使用單個查詢Bags來獲得關(guān)聯(lián)圖。

  • 代碼可在GitHub上獲得 。

參考: Hibernate Fact:從我們的JCG合作伙伴 Vlad Mihalcea的Vlad Mihalcea博客博客中進(jìn)行多級獲取 。

翻譯自: https://www.javacodegeeks.com/2013/11/hibernate-facts-multi-level-fetching.html

總結(jié)

以上是生活随笔為你收集整理的休眠事实:多级访存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。