2012-09-24 4 views
5

Ho il seguente abbastanza semplice a molti rapporti:Hibernate non elimina orfani su OneToMany

squadra ha una serie di giocatori:

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

e ogni giocatore ha un unico id & nome.

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

Ho eseguito un codice molto semplice:

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

E ottengo il seguente come previsto:

  • Hibernate: selezionare team_.id, team_.team_name come team2_0_ da TEAM team_ dove team_.id =?
  • Hibernate: selezionare player_.id, player_.player_name come player2_1_ dal player PLAYER dove player_.id =?
  • Hibernate: selezionare player_.id, player_.player_name come player2_1_ dal player PLAYER dove player_.id =?
  • Hibernate: selezionare player_.id, player_.player_name come player2_1_ dal player PLAYER dove player_.id =?
  • Hibernate: insert into TEAM (TEAM_NAME, id) valori
  • Hibernate (,??): Inserire nel PLAYER (PLAYER_NAME, id) valori
  • Hibernate (,??): Insert into PLAYER (PLAYER_NAME, id) valori (?,?)
  • Ibernazione: inserire nei valori PLAYER (player_name, id) (?,?)
  • Hibernate: aggiornamento PLAYER set TEAM_ID =? dove id =? Hibernate: aggiornamento PLAYER set TEAM_ID =? dove id =? Hibernate: aggiornamento PLAYER set TEAM_ID =? dove id =?

Poi più tardi lo faccio:

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

e si aspettano i giocatori orfani da eliminare ma ottengo:

  • Hibernate: selezionare team_.id, team_.team_name come team2_0_ da TEAM team_ where team_.id =?
  • Hibernate: selezionare player_.id, player_.player_name come player2_1_ dal player PLAYER dove player_.id =?
  • Hibernate: selezionare player_.id, player_.player_name come player2_1_ dal player PLAYER dove player_.id =?
  • Hibernate: aggiornamento PLAYER set TEAM_ID = null dove TEAM_ID =?
  • Hibernate: aggiornamento PLAYER set TEAM_ID =? dove id =?
  • Hibernate: aggiornamento PLAYER set TEAM_ID =? dove id =?

Quale lascia disconnesso il lettore orfano (id = 3) ma non cancellato ... Qualche idea su cosa faccio di sbagliato?

+1

Cosa sta facendo il metodo 'createTeam()'? – dcernahoschi

+0

crea una squadra con i giocatori –

+0

Potrebbe essere correlato o meno ma lo testiamo su HSQL –

risposta

0

Aggiungi attributo mappedBy in relazione a entrambe le entità.

Aggiungi squadra al giocatore.

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

E MappeedBy nel lettore.

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

Quando si ha una relazione 1-A-M, il figlio dovrebbe avere un riferimento del suo genitore. Quindi ibernare internamente utilizza l'id del genitore come straniero nella tabella figlio.

La tabella bambino avrebbe queste 3 colonne:

id , player_name,team_id 
+1

Non c'è motivo di rendere l'associazione bidirezionale.E comunque, in un'associazione bidirezionale, solo uno dei lati deve avere l'attributo mappedBy, poiché significa: "vai a vedere la mappatura per l'associazione sull'altro lato". –

3

Se si vuole che i giocatori vengono rimossi come delete-orphan, è necessario che i giocatori perdono il riferimento alla squadra e il salvare la squadra.

Quello che state facendo è la seguente:

  • Creare un nuovo team di oggetto.
  • Nutrire la squadra con 3 giocatori
  • Persist

Dopo di che, ogni riga giocatore conterrà un FK a squadra (id = 1).

Quindi il codice crea una nuova squadra, con lo stesso id, alimenta 2 giocatori e persiste.

A quel punto ci saranno ancora un giocatore nel DB che i riferimenti alla squadra 1.

Dal mio POV ogni oggetto di business diverso dovrebbe avere la propria chiave di business. Se vuoi sovrascrivere i giocatori della squadra 1, devi prima recuperare la squadra dove id = 1, e quindi nutrire i giocatori.

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, un altro consiglio. Non permettere di modificare direttamente i tuoi giocatori, che possono portare a HibernateErrors come noi "Non modificare il riferimento a una collezione ...". Invece di setPlayers(), aggiungere metodi per addPlayer() e removePlayer()

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

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

Inoltre, una collezione è mutevole, in modo da fare getPlayers() per restituire un insieme non modificabile:

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

Spero che questo getta un po ' luce :)

1

È possibile utilizzare questo tag: @ org.hibernate.annotations.Cascade (value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN). Così si ottiene:

@OneToMany (cascade = {} CascadeType.ALL, orphanRemoval = true) @ org.hibernate.annotations.Cascade (value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN) @JoinColumn (nome = "TEAM_ID")