在JPA / Hibernate中建模两对多关系

时间:2010-02-01 15:56:06

标签: java hibernate jpa orm

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

据我所知,这是一种二对多的关系。但是......我不知道如何在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;}
}

但是,正如您所看到的,我不确定如何从注释方面将表链接在一起。有没有人曾经做过这样的事情?任何想法,帮助?

非常感谢!

5 个答案:

答案 0 :(得分:6)

我希望有人为此提出一个很棒的解决方案,但这是一个棘手的情况,我从来没有找到一种很好的地图。您的选择包括:

  1. 更改关系建模的方式。例如,您可以使用以下内容:

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

    然后GameCollection<GameMembership>,即您将其建模为多对多。 Game仍然可以设置方便的方法来设置第1组和第2组等(业务逻辑强制执行只有2个小组,但是已经完成)但是它们会映射回Collection所使用的冬眠。

  2. 放弃让双向关系 - 选择一个方向(GameTeam似乎是最合适的)并且只打算关系。找到Games a Team然后成为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侧的属性应该映射为正常的多对一关系;然后在mappedBy端使用Team表示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个属性是透明的。

答案 1 :(得分:2)

如果您的游戏拥有第一个玩家 - 一个团队(从团队列表中选择)和第二个玩家 - 一台计算机(从计算机列表中选择),将会发生什么:

  • 您的第一个玩家将成为球队表中的外键。
  • 您的第二个玩家将是计算机表中的外键。

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

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

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

和第二个:

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

答案 2 :(得分:2)

你有一个@OneToMany关系(我想Game与团队有@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;
    }

}

请注意,有时你必须亲自做你的工作。所以封装可能是你问题的关键。

的问候,

答案 3 :(得分:1)

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

答案 4 :(得分:0)

这会有效吗?

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>Private Page</h1>
</body>
</html>