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

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

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

@Entity public class Team extends BaseObject { private Long id; private Set games; @Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() {return id;} public void setId(Long id) {this.id = id;} @OneToMany(mappedBy = "game") public Set getGames() {return games;} public void setGames(Set 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;} } 

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

非常感谢!

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

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

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

    然后Game有一个Collection ,即你将它建模为多对多。 Game仍然可以有方便的方法来设置Team 1和Team 2等(业务逻辑强制执行只有2个团队,但是已经完成)但是它们映射回Hibernate使用的Collection

  2. 放弃让双向关系 – 选择一个方向( GameTeam似乎是最合适的)并且只打算那种关系。 找到一个Team参与的Games然后变成一个来自你的DAO等的操作,而不是可以从Team本身访问的东西:

     public class GameDAO { .... public Collection 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 team1Games; @OneToMany(mappedBy="team2") private Set team2Games; 

    然后为你的API提供便利属性( team1Gamesteam2Games仅供Hibernate使用):

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

    所以对于你class级的来电者来说,有2个属性是透明的。

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

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

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

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

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

和第二个:

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

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

使用封装来实现目标

 @Entity public class Team { private Game game1; private Game game2; private List gameList = new ArrayList(); 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 getGameList() { return this.gameList; } } 

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

问候,

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

这会工作:

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