views:

418

answers:

4

Hello everybody.

Consider the following simple example: One team has many players and a player may only belong to one team.

@Entity
public class Team {

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "team")
    @Cascade({  org.hibernate.annotations.CascadeType.ALL,
                org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
    private Set<Player> player = new HashSet<Player>();

}

@Entity
public class Player {

    @NotNull
    @ManyToOne(targetEntity = Team.class)
    @JoinColumn(name = "team_id")
    private Team team;

}

What I want to achive is moving all players of team A to team B and delete team A afterwards. I do a loop over the players from team A and set their team to team B (here: "this"):

Iterator<Player> iterator = teamA.getPlayer().iterator();
while(iterator.hasNext()){
 Player player = iterator.next();
 player.setTeam(this);
    player.merge();
}
teamA.remove();

The flush is done after that (autoflush), but I also tried to flush before teamA.remove(). This code runs without an error, but afterwards the players from team A are removed from my datasource due to the cascade configuration, as teamA.remove() leads to the removal of all players from team A.

I wonder why they are still associated with team A, because I also tried (and checked in the debugger) to remove all players from the Set, so when teamA.remove() is called, the set is empty. The exceptions thrown in those cases either stated "deleted entity passed to persist" or "detached entity passed to persist". It does work of course, if I create a new transient player, copy all properties and .persist().

How to do this by simply "relinking" the relationship?

Thanks

Wolfram

A: 

I think you have to empty the set before calling remove on teamA. But you said you tried that?

Is the new team a persisted entiy?

cws
Yes, both teams are persistent. I did empty the set, but that might not be carried into the Hibernate session. I also did call .merge() and .flush() in the places where it might make sense. Still, in the end (teamA.remove()), those players seem to be still associated with team A.
Wolfram
A: 

I think the culprit is the DELETE_ORPHAN. From the Annotations reference:

DELETE_ORPHAN applies only to @OneToMany associations, and indicates that the delete()/remove() operation should be applied to any child object that is removed from the association. In other words, if a child is dereferenced by a persistent parent and if DELETE_ORPHAN is used, the "orphaned" child is deleted.

But this isn't what you want since you're "reparenting" the Player.

gnovark
Yes, I want the cascading to be able to remove a team + its players by simply .remove()ing the team (or empty it by .clear()ing the set). It's exactly that "reparenting" that I am stuck with.
Wolfram
Well, thinking about it, I wonder if it is really possible to reparent, as the child will be dereferenced by the parent, regardless of whether it gets a new parent or not. So I might have to go with a new transient object or with removing the DELETE_ORPHAN.
Wolfram
A: 

Have you tried:

team2.setPlayers(team1.getPlayers();
team1.setPlayers(null);
for (Player player : team2.getPlayers() {
   player.setTeam(team2);
}
Bozho
I actually need to merge both teams (not override one) and in my case I have another condition to check for each player so I went with the loop.
Wolfram
A: 

I think that after you change the team of the players, you should call flush(), then call refresh() on teamA and then call remove() on teamA.

Padmarag

related questions