2012-09-24 28 views
5

Tengo el siguiente uno bastante simple de muchas relaciones:Hibernate no elimina los huérfanos en OneToMany

equipo tiene un conjunto de jugadores:

@Entity(name = "TEAM") 
@Access(AccessType.PROPERTY) 
public class Team{ 
    private Integer id; 
    private String name; 
    private Set<Player> players ; 

    @Id 
    @Column(name = "id") 
    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    @Column(name = "team_name") 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    @OneToMany(cascade = {CascadeType.ALL},orphanRemoval=true) 
    @JoinColumn(name = "TEAM_ID") 
    public Set<Player> getPlayers() { 
     return players; 
    } 

    public void setPlayers(Set<Player> players) { 
     this.players = players; 
    }  
} 

Y tiene un nombre único Identificación del & cada jugador.

@Entity(name = "PLAYER") 
@Access(AccessType.PROPERTY) 
public class Player implements Serializable{ 

    private int id; 
    private String name; 

    @Id 
    @Column(name = "id") 
    public int getId() { 
     return id; 
    } 
    public void setId(int id) { 
     this.id = id; 
    } 

    @Column(name = "player_name") 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    @Override 
    public boolean equals(Object obj) { 
    return id == ((Player)obj).id; 
    } 
    @Override 
    public int hashCode() { 
     return id; 
    } 
} 

corro un código muy simple:

Team team = createTeam(3) // creates team with 3 players ids={1,2,3} 
session.saveOrUpdate(team); 
... 

private Team createTeam(int players) { 
    Team team = new Team(); 
    team.setName("Bears"); 
    team.setId(1); 
    for(int i=1 ; i<=players; ++ i){ 
     Player player = new Player(); 
     player.setId(i); 
     player.setName("Player"+i); 
     team.addPlayer(player); 
    } 
    return team; 
} 

Y me sale el siguiente como se esperaba:

  • Hibernate: seleccione team_.id, team_.team_name como team2_0_ de team_ EQUIPO donde team_.id =?
  • Hibernar: selecciona player_.id, player_.player_name como player2_1_ de PLAYER player_ donde player_.id =?
  • Hibernar: selecciona player_.id, player_.player_name como player2_1_ de PLAYER player_ donde player_.id =?
  • Hibernar: selecciona player_.id, player_.player_name como player2_1_ de PLAYER player_ donde player_.id =?
  • Hibernate: insertar en TEAM (TEAM_NAME, ID) valores
  • Hibernate (?,?): Insertar en el reproductor (PLAYER_NAME, id) valores
  • Hibernate (?,?): Insertar en Player (PLAYER_NAME, ID) values ​​(?,?)
  • Hibernate: insertar en los valores de PLAYER (player_name, id) (?,?)
  • Hibernate: update PLAYER set TEAM_ID =? donde id =? Hibernar: ¿actualizar PLAYER establecer TEAM_ID =? donde id =? Hibernar: ¿actualizar PLAYER establecer TEAM_ID =? donde id =?

Luego, más tarde yo:

Team team = createTeam(2) // creates team with 2 player ids={1,2} 
session.saveOrUpdate(team); 

y esperar que los jugadores huérfanos que se eliminarán, pero me sale:

  • Hibernate: seleccione team_.id, team_.team_name como team2_0_ del equipo team_ donde team_.id =?
  • Hibernar: selecciona player_.id, player_.player_name como player2_1_ de PLAYER player_ donde player_.id =?
  • Hibernar: selecciona player_.id, player_.player_name como player2_1_ de PLAYER player_ donde player_.id =?
  • Hibernar: actualizar PLAYER establecer TEAM_ID = null donde TEAM_ID =?
  • Hibernar: ¿actualizar PLAYER establecer TEAM_ID =? donde id =?
  • Hibernar: ¿actualizar PLAYER establecer TEAM_ID =? donde id =?

Que deja al jugador huérfano (id = 3) desconectado pero no eliminado ... ¿Alguna idea de lo que hago mal?

+1

¿Qué está haciendo el método 'createTeam()'? – dcernahoschi

+0

crea un equipo con jugadores –

+0

Puede estar relacionado o no pero lo probamos en HSQL –

Respuesta

0

Agrega el atributo mappedBy en la relación de ambas entidades.

Agregar equipo en el reproductor.

// in Player.java 
@ManyToOne(mappedBy="players") 
private Team team; 

Y MappeedBy en Reproductor.

//In Team.java 
@OneToMany(cascade = {CascadeType.ALL},orphanRemoval=true,mappedBy="team") 
    @JoinColumn(name = "TEAM_ID") 
    public Set<Player> getPlayers() { 
     return players; 
    } 

Cuando tiene una relación 1-A-M, el niño debe tener una referencia de su elemento primario. Luego hibernate internamente usa la identificación de los padres como una tabla foránea en el niño.

Su tabla secundaria tendría estas 3 columnas:

id , player_name,team_id 
+1

No hay razón para hacer que la asociación sea bidireccional.Y de todos modos, en una asociación bidireccional, solo uno de los lados debe tener el atributo mappedBy, ya que significa: "ve a ver el mapeo de la asociación en el otro lado". –

3

Si desea que los jugadores se retiran como eliminar huérfano, es necesario que los jugadores pierdan la referencia al equipo y el salvar al equipo.

Lo que está haciendo es el siguiente:

  • crear un nuevo equipo objeto.
  • Alimentar el equipo con 3 jugadores
  • Persistir

Después de eso, cada fila jugador contendrá un FK al equipo (id = 1).

Luego el código crea un nuevo equipo, con la misma identificación, y alimenta a 2 jugadores y persiste.

En ese momento todavía habrá un jugador en el PP que las referencias a la del equipo 1.

Desde mi Punto de vista todos los objetos de negocio diferente debe tener su propia clave de negocio. Si desea sobrescribir a los jugadores del equipo 1, primero debe recuperar el equipo donde id = 1, y luego alimentar a los jugadores.

private Team createTeam(int players) { 
    Team team = session.get(Team.class, 1); 
    if (team == null) { 
     team = new Team(); 
     team.setName("Bears"); 
     team.setId(1); 
    } 
    team.clearPlayers(); 

    for(int i=1 ; i<=players; ++ i){ 
     Player player = new Player(); 
     player.setId(i); 
     player.setName("Player"+i); 
     team.addPlayer(player); 
    } 
    return team; 
} 

// Team.java 
private void clearPlayers() { 
    players.clear(); 
} 

BTW, otro consejo. No permita modificar sus reproductores directamente, lo que puede ocasionar HibernateErrors como "No cambie la referencia a una colección ...". En lugar de setPlayers(), agregue métodos para addPlayer() y removePlayer()

private void adddPlayer(Player player) { 
    player.setTeam(this); 
    players.add(player); 
} 

private void removePlayer(Player player) { 
    player.setTeam(null); 
    players.remove(player); 
} 

Además, una colección es mutable, así que getPlayers() para devolver una colección no modificables:

private Set<Player> getPlayers() { 
    return Collections.unmodifiableSet(players); 
} 

Hope esto arroja alguna luz :)

1

Puede utilizar esta etiqueta: @ org.hibernate.annotations.Cascade (value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN). Por lo que se obtiene:

@OneToMany (cascada = {} CascadeType.ALL, orphanRemoval = true) @ org.hibernate.annotations.Cascade (valor = org.hibernate.annotations.CascadeType.DELETE_ORPHAN) @JoinColumn (nombre = "TEAM_ID")

Cuestiones relacionadas