神奇好望角 The Magical Cape of Good Hope

          庸人不必自擾,智者何需千慮?
          posts - 26, comments - 50, trackbacks - 0, articles - 11
            BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

          JPA 應用技巧 3:映射多對多的關聯表

          Posted on 2011-09-27 11:04 蜀山兆孨龘 閱讀(3268) 評論(2)  編輯  收藏 所屬分類: Java EE

          實體間的多對多的關聯需要一張關聯表。如果直接使用 ManyToMany 來映射,JPA 就會隱式地幫我們自動管理關聯表,代碼寫出來和其他類型的關聯差別不大。例如,某州炒房團需要一個炒房跟蹤系統,那么該系統中的炒房客和房子就是多對多的關系:

                  public class Speculator implements Serializable {
                      @Id
                      private Integer id;
                      @ManyToMany
                      @JoinTable(joinColumns = @JoinColumn(name = "speculator_id"),
                              inverseJoinColumns = @JoinColumn(name = "house_id"))
                      private List<House> houses;
                      // 此處省略若干行
                  }
          
                  public class House implements Serializable {
                      @Id
                      private Integer id;
                      @ManyToMany(mappedBy = "houses")
                      private List<Speculator> speculators;
                      // 此處省略若干行
                  }
              

          如果炒房客 s 要賣掉房子 h(嚴格點說是賣掉房子的產權部分),那么系統執行的代碼差不多就是 s.getHouses().remove(h)??此坪唵?,然而底層的操作卻性能低下:JPA 會先從數據庫中取出炒房客的所有房產(s.getHouses()),然后再刪除指定的那套房子;從數據庫層面上看,這將先從關聯表(speculator_house)中找到該炒房客的所有房子的外鍵,然后從 house 表載入這些 House 對象,最后才從 speculator_house 刪除關聯。在 ORM 出現前,這種操作只需要改關聯表,根本不用關心其他房子。這種簡單的多對多映射寫法將關聯表隱藏起來,雖然簡化了代碼,卻也可能帶來性能隱患。

          很自然地可以想到,如果把關聯表也映射成實體類,就能解決這個問題。speculator_house 包含兩個外鍵,可用作聯合主鍵。如果把它映射為 SpeculatorHouse 類,則該類與 SpeculatorHouse 都是多對一的關系。關聯表實體類的代碼如下(EmbeddedId 的映射技巧見《JPA 應用技巧 2:主鍵外鍵合體映射》):

                  @Embeddable
                  public class SpeculatorHouseId implements Serializable {
                      private Integer speculatorId;
                      private Integer houseId;
                      // 此處省略若干行
                  }
          
                  @Entity
                  @Table(name = "speculator_house")
                  public class SpeculatorHouse implements Serializable {
                      @EmbeddedId
                      private SpeculatorHouseId id;
                      @MapsId("speculatorId")
                      @ManyToOne
                      private Speculator speculator;
                      @MapsId("houseId")
                      @ManyToOne
                      private House house;
                      // 此處省略若干行
                  }
              

          SpeculatorHouse 也要增加相應的關聯信息:

                  public class Speculator implements Serializable {
                      @Id
                      private Integer id;
                      @ManyToMany
                      @JoinTable(joinColumns = @JoinColumn(name = "speculator_id"),
                              inverseJoinColumns = @JoinColumn(name = "house_id"))
                      private List<House> houses;
                      @OneToMany(mappedBy = "speculator")
                      private List<SpeculatorHouse> speculatorHouses;
                      // 此處省略若干行
                  }
          
                  public class House implements Serializable {
                      @Id
                      private Integer id;
                      @ManyToMany(mappedBy = "houses")
                      private List<Speculator> speculators;
                      @OneToMany(mappedBy = "house")
                      private List<SpeculatorHouse> speculatorHouses;
                      // 此處省略若干行
                  }
              

          這樣既保留了多對多關系,又映射了關聯表,然后就可以根據實際情況選擇隱式或顯示的關聯表管理。例如,要得到一個炒房客的全部房子,就使用隱式管理:s.getHouses();而要刪除炒房客和某套房子的關聯,則用顯示管理:delete from SpeculatorHouse sh where sh.speculator = :s and sh.house = :h。


          評論

          # re: JPA 應用技巧 3:映射多對多的關聯表[未登錄]  回復  更多評論   

          2012-06-14 16:05 by DD
          但是,我按照你這樣配起來,怎么刪speculator?。。我現在無法刪除speculator。

          # re: JPA 應用技巧 3:映射多對多的關聯表  回復  更多評論   

          2012-10-11 15:06 by merge
          對于中間表操作描述詳細,收藏了!
          主站蜘蛛池模板: 湖南省| 获嘉县| 镇江市| 庄浪县| 师宗县| 分宜县| 兰州市| 敖汉旗| 永顺县| 龙山县| 宜川县| 贺兰县| 灵武市| 若尔盖县| 邛崃市| 炉霍县| 乡城县| 林州市| 伊金霍洛旗| 大兴区| 林甸县| 定西市| 嘉鱼县| 洪洞县| 开江县| 建瓯市| 阿克| 奉新县| 华池县| 五莲县| 托里县| 安徽省| 博罗县| 梁河县| 霍城县| 阿鲁科尔沁旗| 云浮市| 湟源县| 张家川| 彰化市| 西盟|