Babak Behzadi
Babak Behzadi

Reputation: 1256

ManyToMany Relation Strange Behavior

I have two entities 'Person' and 'Team' which are mapped by a many-to-many relation using annotations, as follow:

@Entity
public class Perosn implements Serializable {
      ....
      private Collection<Team> teams;
      ....

     @ManyToMany(fetch = FetchType.LAZY)
     @Cascade({CascadeType.ALL})
     @JoinTable(name = "person_team",
            joinColumns = { @JoinColumn(name = "person_id")},
            inverseJoinColumns = {@JoinColumn(name = "team_id")})
     public Collection<Team> getTeams() {
        return advisers;
     }
}

@Entity
public class Team implements Serializable {
      ....
      private Collection<Person> persons;
      ....

     @ManyToMany(fetch = FetchType.LAZY)
     @Cascade({CascadeType.ALL})
     @JoinTable(name = "person_team",
            joinColumns = { @JoinColumn(name = "team_id")},
            inverseJoinColumns = {@JoinColumn(name = "person_id")})
     public Collection<Person> getPersons() {
        return advisers;
     }
}

I use following service class to add a new team to a person object and update join table(add new record containing personId and teamId):

@Service("teamService")
public class TeamService extends DataAccess {
    ....
    public void addTeamToPerson(String teamId, String personId) {

        Team team= getTeam(teamId);
        Person person= personService.getPerson(personId);
        person.getTeams().add(team);
        super.update(team);
    }
    ....
}

public abstract class DataAccess<E> {

    private EntityManager manager;
    ....
    public void update(E object) {
        try {
            manager.getTransaction().begin();
            manager.merge(object);
            manager.getTransaction().commit();
        } catch (Exception e) {
            ....
        }
   }
   ....
}

This structure works truly but I think it's strange adding an team object to team collection of person object and updating the team object. Is it wrong logically? I need to know there is a master-slave feature or something like that in this type of relation.

Any guide will be regarded.

Upvotes: 0

Views: 195

Answers (1)

JB Nizet
JB Nizet

Reputation: 692151

Your mapping is wrong: instead of defining a many-to-many bidirectional association, you're defining two many-to-many unidirectional associations, using the same join table.

A bidirectional association always has an owner side and an inverse side. The owner side is the one without the mappedBy attribute. The inverse side must have a mappedBy attribute. Think about it: why would you need to tell Hibernate twice how a bidirectional association is mapped?

The mapping should be:

@Entity
public class Person implements Serializable {
      ....
      private Collection<Team> teams;
      ....

     @ManyToMany(fetch = FetchType.LAZY)
     @Cascade({CascadeType.ALL})
     @JoinTable(name = "person_team",
            joinColumns = { @JoinColumn(name = "person_id")},
            inverseJoinColumns = {@JoinColumn(name = "team_id")})
     public Collection<Team> getTeams() {
        return advisers;
     }
}

@Entity
public class Team implements Serializable {
     ....
     private Collection<Person> persons;
     ....

     @ManyToMany(fetch = FetchType.LAZY, mappedBy = "teams")
     @Cascade({CascadeType.ALL})
     public Collection<Person> getPersons() {
        return advisers;
     }
}

In this case, the owner side is Person. This means that to add an association between a person and a team, you need to add the team to the teams collection of the person. You may choose to map it the other way, and in this case, you'll need to add the person to the team collection of persons.

Also, note that having CascadeType.ALL on a ManyToMany association is really dubious: do you really want to delete all the persons when you delete a team, and to delete all the teams of the deleted persons, etc.?

Upvotes: 1

Related Questions