Моделирование отношений "два-многие" в JPA/Hibernate
У меня есть следующая проблема отношения сущности. "Игра" должна иметь два (и только два) объекта "Команда". "Команда" может иметь много "игр"
Это, насколько я вижу, является отношением "два-ко-многим". Однако... Я не знаю, как моделировать это в 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;}
}
Но, как вы можете видеть, я не уверен, как связать таблицы вместе с аннотацией. Кто-нибудь когда-либо делал что-то подобное раньше? Любые идеи, помощь?
Большое спасибо!
Ответы
Ответ 1
Мне бы очень хотелось, чтобы кто-то придумал удивительное решение этого, но это сложная ситуация, в которой я никогда не мог найти способ очень хорошо отобразить карту. Ваши варианты включают:
-
Измените способ моделирования отношений. Например, у вас может быть что-то вроде:
@Entity
public class GameMembership {
Team team;
Game game;
int gamePosition; // If tracking Team 1 vs Team 2 matters to you
}
а затем Game
имеет a Collection<GameMembership>
, т.е. вы моделируете его как много-ко-многим. Game
могут по-прежнему иметь удобные методы для установки Team 1 и Team 2 и т.д. (бизнес-логика для обеспечения того, что есть только две команды, но это сделано), но они вернутся к Collection
, используемому Hibernate.
-
Откажитесь от того, чтобы соотношение было двунаправленным - выберите одно направление (Game
→ Team
кажется наиболее подходящим) и сочтет только эти отношения. Поиск в 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();
}
}
или что-то подобное...
-
Продолжайте движение по маршруту, но "обманите" в конце Team
. Свойства со стороны Game
должны отображаться как нормальные отношения "много-к-одному"; затем использовал mappedBy
в конце Team
, чтобы указать, что Game
'управляет соотношением.
public class Team {
...
@OneToMany(mappedBy="team1")
private Set<Game> team1Games;
@OneToMany(mappedBy="team2")
private Set<Game> team2Games;
а затем у вас есть свойство удобства для вашего API (team1Games
и team2Games
предназначены только для использования 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")
Ответ 3
У вас есть отношения @OneToMany (я полагаю, что у Game есть отношения @ManyToOne с Team), мой совет:
Используйте инкапсуляцию, чтобы получить свою цель.
@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
Я думаю, что у вас есть два отношения один-ко-многим, а не два-ко-многим.
Ответ 5
Будет ли это работать:
@OneToMany
@JoinFormula(value = "SELECT g.id FROM game g WHERE g.homeTeam = id or g.awayTeam=id")
private Set<Game> games;