Моделирование отношений "два-многие" в 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.

  • Откажитесь от того, чтобы соотношение было двунаправленным - выберите одно направление (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();
        }
    }
    

    или что-то подобное...

  • Продолжайте движение по маршруту, но "обманите" в конце 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;