在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;} }
但是,正如您所看到的,我不确定如何从注释端将表链接在一起。 有没有人曾经做过这样的事情? 任何想法,帮助?
非常感谢!
我希望有人能够为此提出一个很棒的解决方案,但这是一个棘手的情况,我从来没有找到一种方法来很好地映射。 您的选择包括:
-
改变你建立关系的方式。 例如,你可以有类似的东西:
@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
。 -
放弃让双向关系 – 选择一个方向(
Game
→Team
似乎是最合适的)并且只打算那种关系。 找到一个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(); } } 或类似的东西……
-
沿着你正在走的路线继续前行,但在
Team
结束时“欺骗”。Game
方面的属性应该映射为正常的多对一关系; 然后在Team
端使用mappedBy
来表示Game
‘控制’这种关系。public class Team { ... @OneToMany(mappedBy="team1") private Set
team1Games; @OneToMany(mappedBy="team2") private Set team2Games; 然后为你的API提供便利属性(
team1Games
和team2Games
仅供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;
- 使用GSON在字符串和byte 之间转换JSON
- Hibernate单向父/子关系 – delete()对子表而不是删除执行更新
- Hibernate OnDelete Cascade不适用于MySql,但适用于postgres和Ms-Sql
- 如何在同一个数据库表上映射两个JPA或Hibernate实体
- 使用SequenceGenerator的javax.persistence.EntityExistsException
- JPA @TableGenerator在多个实体之间共享
- Java JPA GenerationType.Auto值始终为null
- 无法从另一个maven子项目调用Hibernate / QueryDSL
- Netbeans Hibernate映射文件中的NullPointerException和Firebird数据库中的POJO向导