I have the following pretty simple one to many relations:
Team has a Set of players:
@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;
}
}
And each player has a unique id & name.
@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;
}
}
I run a very simple code:
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;
}
And I get the following as expected:
Then later I do:
Team team = createTeam(2) // creates team with 2 player ids={1,2}
session.saveOrUpdate(team);
And expect the orphan players to be deleted but I get:
Which leaves the orphan player (id=3) disconnected but not deleted... Any ideas what I do wrong?
If you want that the players are removed as delete-orphan, you need that the players loose the reference to the team and the save the team.
What you are doing is the following:
After that, each player row will contain a FK to team (id=1).
Then the code creates a new team, with the same id, and feed 2 players and persist.
At that point there still will be a player in DB that references to team 1.
From my POV every different business object should have their own business key. If you want to overwrite the players of team 1, you should first retrieve team where id = 1, and then feed the players.
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, another advice. Don't allow to modify your players directly, that can lead to HibernateErrors such us "Don't change the reference to a collection...". Instead of setPlayers(), add methods for addPlayer()
and removePlayer()
private void adddPlayer(Player player) {
player.setTeam(this);
players.add(player);
}
private void removePlayer(Player player) {
player.setTeam(null);
players.remove(player);
}
Also, a collection is mutable, so make getPlayers() to return a non-modifiable collection:
private Set<Player> getPlayers() {
return Collections.unmodifiableSet(players);
}
Hope this sheds some light :)
You could use this tag: @org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN). So you get:
@OneToMany(cascade = {CascadeType.ALL},orphanRemoval=true) @org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN) @JoinColumn(name = "TEAM_ID")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With