【问题标题】:Modelling a Two to Many Relationship in JPA/Hibernate在 JPA/Hibernate 中建模一对多关系
【发布时间】:2010-02-01 15:56:06
【问题描述】:

我有以下实体关系问题。 “游戏”必须有两个(并且只有两个)“团队”对象。一个“团队”可以有很多“游戏”

据我所知,这是一个二对多的关系。但是...我不知道如何在 JPA 中对此进行建模。例如,我打算做这样的事情......

@Entity
public class Team extends BaseObject {
  private Long id;
  private Set<Game> games;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO) 
  public Long getId() {return id;}
  public void setId(Long id) {this.id = id;}

  @OneToMany(mappedBy = "game")
  public Set<Game> getGames() {return games;}
  public void setGames(Set<Game> games) {this.games = games;}
}

@Entity
public class Game extends BaseObject {
  private Long id;
  private Team team1;
  private Team team2;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO) 
  public Long getId() {return id;}
  public void setId(Long id) {this.id = id;}

  @ HERE IS THE PROBLEM - WHAT ANNOTATION DO I USE?
  public Team getTeam1() {return team1;}
  public void setTeam1(Team team1) {this.team1 = team1;}

  @ HERE IS THE PROBLEM - WHAT ANNOTATION DO I USE?
  public Team getTeam2() {return team2;}
  public void setTeam2(Team team1) {this.team2 = team2;}
}

但是,如您所见,我不确定如何从注释端将表格链接在一起。有没有人做过这样的事情?有什么想法,帮忙?

非常感谢!

【问题讨论】:

    标签: java hibernate jpa orm


    【解决方案1】:

    我希望有人能想出一个很棒的解决方案来解决这个问题,但这是一个棘手的情况,我一直无法找到很好的映射方法。您的选择包括:

    1. 改变建立关系的方式。例如,你可以有类似的东西:

      @Entity
      public class GameMembership {
         Team team;
         Game game;
         int gamePosition; // If tracking Team 1 vs Team 2 matters to you
      }
      

      然后Game 有一个Collection&lt;GameMembership&gt;,即您将其建模为多对多。 Game 仍然可以有方便的方法来设置团队 1 和团队 2 等(业务逻辑强制只有 2 个团队,但是这样做了)但它们映射回 Hibernate 使用的 Collection

    2. 放弃双向关系——选择一个方向(GameTeam 似乎是最合适的)并只打盹那个关系。找到 GamesTeam 参与然后成为您的 DAO 等的操作,而不是可以从 Team 本身访问的操作:

      public class GameDAO {
          ....
          public Collection<Game> gamesForTeam(Team t) {
               ....
               Query q = session.createQuery("FROM Game WHERE team1 = :team OR team2 = :team");
               q.setParameter("team", t);
               return q.list();
          }
      }
      

      或类似的东西......

    3. 沿着你要走的路继续前行,但在Team 结束时“作弊”。 Game 侧的属性应该映射为正常的多对一关系;然后在Team 末尾使用mappedBy 表示Game '控制` 关系。

      public class Team {
              ...
              @OneToMany(mappedBy="team1")
              private Set<Game> team1Games;
              @OneToMany(mappedBy="team2")
              private Set<Game> team2Games;
      

      并且那么为您的 API 提供了一个便利属性(team1Gamesteam2Games 仅供 Hibernate 使用):

          @Transient
          public Set<Game> getGames() {
              Set<Game> allGames = new HashSet<Game>(team1Games);
              allGames.addAll(team2Games);
              // Or use google-collections Sets.union() for bonus points
              return allGames;
          }
      

      所以对于你的类的调用者来说,有 2 个属性是透明的。

    【讨论】:

    • 我同意...这似乎是最全面的答案。我想我最终将不得不选择你的第二个选项。谢谢!
    【解决方案2】:

    考虑一下如果您的游戏有第一个玩家 - 一个团队(从团队列表中选择)和第二个玩家 - 一台计算机(从计算机列表中选择):

    • 您的第一个球员将是球队表的外键。
    • 您的第二个玩家将是进入计算机表的外键。

    如果您现在将“计算机”作为玩家替换为另一个“团队”,您将在团队表中获得两个外键。

    我的 JPA 有点生疏,但我相信您使用 @OneToOne 注释对外键关系建模,如下所示:

    @OneToOne(cascade = {CascadeType.ALL}, optional = false)
    @JoinColumn(name = "team1")
    

    第二个是:

    @OneToOne(cascade = {CascadeType.ALL}, optional = false)
    @JoinColumn(name = "team2")
    

    【讨论】:

    • 嗨,劳拉,这对我来说很有意义。但是,我想知道您是否知道这对关系的另一方有什么影响……即,从 Team 对象,当我们调用 Set getGames() 方法时……即,当我去调用“ team.getGames" 关系是否足够智能,可以检查该团队在所有游戏中是 team1 还是 team2?希望这是有道理的,并感谢您的帮助。
    • 是的,它会 - 在数据库级别完成。这是因为映射完全不同。在games 表中,每行将有两列,其中包含teams 表中的ID(这些是外键) - 简单的一对一。在teams 表中,由于一个团队可以参与许多游戏,因此您将拥有一对五的关系。这意味着将创建另一个类似于(id, game_id, team_id) 的表,并且在teams 表中,您将拥有来自这个额外表的id,而不是来自gamesid。请注意,您不会自己管理额外的表格。
    • 嗨劳拉,如果我没记错的话,你的额外表似乎是多对多解决方案中的链接表。当您说球队与比赛是一对多的关系时,我同意,但我不确定一对多是否通常需要链接表。这听起来不像是多对多的解决方案吗?此外,除非我弄错了,否则休眠不会自动创建该链接表。感谢您的帮助!
    • 从数据库的角度来看,这是一个ManyToMany关系。这是关系数据库和 OOP 之间的不匹配之一。
    • 你当然是对的——我展示的确实是一个多对多映射(对我来说已经很晚了,抱歉)。我不知道在这种情况下映射将如何起作用。我想说的是,由于 PK 在这种情况下的工作方式,您无论如何都需要一个额外的表。
    【解决方案3】:

    你有一个@OneToMany 关系(我想Game 和Team 有一个@ManyToOne 关系),我的建议是:

    使用封装来实现你的目标

    @Entity
    public class Team {
    
        private Game game1;
        private Game game2;
    
        private List<Game> gameList = new ArrayList<Game>();
    
        public void setGame1(Game game1) {
            // You can use index 0 to store your game1
            if(getGameList().size == 0)
                getGameList().add(game1);
            else
                getGameList().set(0, game1);
        }
    
        @Transient
        public Game getGame1() {
            if(getGameList().size() == 0)
                return null;
    
            return getGameList().get(0);
        }
    
        public void setGame2(Game game2) {
            // You can use index 1 to store your game2
            switch(getGameList().size()) {
                case 0:
                    getGameList().add(null);
    
                    getGameList().add(game2);
                break;
                case 1:
                    getGameList().add(game2);
                break;
                case 2:
                    getGameList().set(1, game2);
                break;
            }
        }
    
        @Transient
        public Game getGame2() {
            if(getGameList().size() < 2)
                return null;
    
            return getGameList().get(1);
        }
    
        @OneToMany
        @JoinColumn(name="TEAM_ID")
        public List<Game> getGameList() {
            return this.gameList;
        }
    
    }
    

    请注意,有时您必须手动完成工作。所以封装可能是您问题的关键。

    问候,

    【讨论】:

      【解决方案4】:

      我认为你有两个一对多的关系,而不是一个二对多的关系。

      【讨论】:

      • 这是不正确的。他没有两个sets 的团队,他有两个团队。
      【解决方案5】:

      这行得通吗:

      @OneToMany
      @JoinFormula(value = "SELECT g.id FROM game g WHERE g.homeTeam = id or g.awayTeam=id") 
      private Set<Game> games;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-21
        • 1970-01-01
        • 1970-01-01
        • 2017-12-30
        • 2019-02-11
        • 2011-08-04
        相关资源
        最近更新 更多