2010-02-01 22 views
10

Tengo el siguiente problema de relación de entidad. Un "Juego" debe tener dos (y solo dos) objetos de "Equipo". Un "Equipo" puede tener muchos "Juegos"Modelando una relación de dos a muchas en JPA/Hibernate

Esto, por lo que puedo ver es una relación de dos a muchos. Sin embargo ... No sé cómo modelar esto en JPA. Por ejemplo, yo iba a hacer algo como esto ...

@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;} 
} 

Pero, como se puede ver, no estoy seguro de cómo vincular las tablas entre sí desde un lado de la anotación. ¿Alguien ha hecho algo como esto antes? Alguna idea, ayuda?

muchas gracias!

+0

Buena pregunta (1) –

Respuesta

6

lo haría Me encantaría que alguien encontrara una solución increíble para esto, pero es una situación complicada que nunca he sido capaz de encontrar una forma de mapear muy bien. Sus opciones incluyen:

  1. Cambie la forma de modelar la relación. Por ejemplo, usted podría tener algo como:

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

    y luego Game tiene un Collection<GameMembership>, es decir, que modelarlo como muchos-a-muchos. Game aún puede tener métodos convenientes para configurar el Equipo 1 y el Equipo 2, etc. (lógica de negocios para exigir que solo haya 2 equipos, sin embargo, eso está hecho) pero se vuelven a asignar al Collection tal como lo utiliza Hibernate.

  2. Olvídate de tener una relación bidireccional: elige una dirección (GameTeam parece ser la más adecuada) y siesta solo esa relación. Encontrar el Games un Team está involucrado en una operación se convierte entonces en de su DAO etc, en lugar de algo que es accesible desde el propio 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(); 
        } 
    } 
    

    o algo similar ...

  3. continuar por la ruta que está tomando, pero 'engañar' al final Team. Las propiedades en el lado Game deben correlacionarse como relaciones normales de varios a uno; luego usó mappedBy en el extremo Team para indicar que Game 'controla' la relación.

    public class Team { 
         ... 
         @OneToMany(mappedBy="team1") 
         private Set<Game> team1Games; 
         @OneToMany(mappedBy="team2") 
         private Set<Game> team2Games; 
    

    y continuación tienen una propiedad de conveniencia para su API (team1Games y team2Games son sólo para el uso de 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; 
        } 
    

    por lo que las personas que llaman de su clase, es transparente que hay 2 propiedades .

+0

Estoy de acuerdo ...esta parece ser la respuesta más completa. Creo que voy a tener que elegir tu segunda opción. ¡Gracias! – Brian

1

yo creo que hay dos uno-a-muchos, no uno de dos-a-muchos.

+2

Esto es incorrecto. Él no tiene dos 'conjuntos 'de equipos, él tiene dos equipos. – laura

2

Considere qué pasaría si sus juegos tendrían el primer jugador - un equipo (elegido de una lista de equipos) y el segundo jugador - un ordenador (elegido de una lista de equipos):

  • Su El primer jugador sería una llave extranjera en la mesa de equipos.
  • Su segundo jugador sería una clave externa en la tabla de computadoras.

Si ahora reemplazas la "computadora" como jugador con otro "equipo", obtendrías dos llaves foráneas en la mesa de equipos.

Mi APP es un poco oxidado, pero creo que un modelo de una relación de clave externa con una anotación @OneToOne, así:

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

y el segundo con:

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

Hola Laura, eso tiene sentido para mí. Sin embargo, me pregunto ¿sabes qué impacto tiene esto en el otro lado de la relación ..., desde el objeto Team, cuando llamamos al Set getGames() método ... es decir, cuando voy a llame a "team.getGames" ¿será la relación lo suficientemente inteligente como para verificar si este equipo es team1 o team2 en todos los juegos? Espero que esto tenga sentido y gracias por la ayuda. – Brian

+0

Sí lo hará, hecho a nivel de base de datos. Esto se debe a que el mapeo es completamente diferente. En la tabla 'games', tendrías dos columnas en cada fila que tienen una ID de la tabla' teams' (estas son claves foráneas) - simple uno a uno. En la tabla 'equipos', dado que un equipo puede formar parte de muchos juegos, tendrás una relación de 1 a mayo. Esto significa que se creará otra tabla que se verá similar a '(id, game_id, team_id)' y en la tabla 'teams' tendrá el' id' de esta mesa extra, no el 'id' de' games' . Tenga en cuenta que usted no administra la mesa adicional usted mismo. – laura

+0

Hola Laura, tu tabla extra parece ser una tabla de enlaces en una solución ManyToMany si no me equivoco. Cuando dices que los equipos para los juegos son una relación de 1 a muchos, estoy de acuerdo, pero no estoy seguro si uno a muchos requiere una tabla de enlaces normalmente. ¿No suena esto como una solución de muchos a muchos? Además, esa tabla de enlace no se creará automáticamente por hibernación a menos que esté equivocado. ¡gracias por la ayuda! – Brian

2

usted tiene una relación @OneToMany (supongo juego tiene una relación con el equipo @ManyToOne), mi consejo es:

Uso encapsulación para conseguir su objetivo

@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; 
    } 

} 

Tenga en cuenta que a veces tiene que hacer su trabajo mannually. Entonces, la encapsulación puede ser la clave de tu pregunta.

cordiales,

0

Que este trabajo:

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