Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] Problems with ManyToOne and OneToMany

This revelation has been a little disappointing.

I have also run into this problem with JPA (Eclipselink) and I am wondering
about a couple of things I have seen.

Using the same scenario of a parent -> child table relationship as Mr
Johannes except using Collection -

@Entity
public class Team{
     @OneToMany(mappedBy="team")
     public Collection<Player> players;

     ...getter and setters for Team attributes
 }

 @Entity
 public class Player{
     @JoinColumn(name = "team_id", referencedColumnName = "team_id")
     @ManyToOne
     public Team team;

     ...getter and setters for Player attributes
 }

I can get the Player record's foreign key: team_id to populate automatically
during a create operation by doing -

... aTeam is a new instance of the Team class that has not yet been created
in the database
... playerList is a collection of Player objects that have not yet been
created in the datase

em.persist(aTeam);

for (Iterator it=playerList.iterator(); it.hasNext(); ) {
    Player player = (Player) it.next();
    em.persist(player);
    player.setTeam(aTeam);
}

The result of that is a new Team record in the Team table and however many
Player records in the Player table with foreign key values set to the parent
Team record.

However, if I change the code to be like this -

em.persist(aTeam);

for (Iterator it=playerList.iterator(); it.hasNext(); ) {
    Player player = (Player) it.next();
    em.persist(player);
    aTeam.getPlayers().add(player);
}

The new Team record gets created and so do the new Player records except
none of the Player record foreign key values are set to the parent Team
record.  The Player record team_id values are NULL.

Is that just a flaw with the current JPA implementation?  

I have found that Many-to-Many relationships using a mapping table don't
have this problem.
For instance if the Team entity class had another reference to something
like Sponsers where a Sponser could sponser many teams and teams could have
many sponsers you might have this -

@Entity
public class Team{
     @OneToMany(mappedBy="team")
     public Collection<Player> players;
     
     @JoinTable(name = "TEAM_SPONSER_MAP", joinColumns = {@JoinColumn(name =
"TEAM_ID",referencedColumnName = "TEAM_ID")}, inverseJoinColumns =
{@JoinColumn(name = "SPONSER_ID", referencedColumnName = "SPONSER_ID")})
    @ManyToMany
    private Collection<SPONSER> sponsers;

     ...getter and setters for Team attributes
 }

 @Entity
 public class Sponser{
     @ManyToMany(mappedBy = "sponsers", fetch = FetchType.LAZY)
     public Collection<Team> teams;

     ...getter and setters for Sponser attributes
 }

 @Entity
 public class Player{
     @JoinColumn(name = "team_id", referencedColumnName = "team_id")
     @ManyToOne
     public Team team;

     ...getter and setters for Player attributes
 }

 In this case doing an operation like this -

 ....sponserList is a Collection<Sponser> that has been populated with
Sponser objects to be linked to a Team object
.....aTeam is a Team entity that has just been created

 em.persist(aTeam);

 for ( Iterator it = sponserList.iterator(); it.hasNext(); ) {
      Sponser sponser = (Sponser)it.next();
      aTeam.getSponsers().add(sponser);
 }

 The result is the TEAM_SPONSER_MAP table gets automatically populated with
the linked Team and Sponser records.

 One last example of a problem involving the first case of the ManyToOne
mapping -
In the case of an update where a Team entity is retrieved from the database
and includes a populated collection of team Players.  If an edit operation
removes a player (or more than one player) from the Team object "players"
collection and an entity manager is used to update the Team using a merge:

em.merge(aTeam);  ....where aTeam is the entity object the edit operation
manipulated

The merge process will not handle the players collection properly and the
players removed from the aTeam object do not get deleted.  The remove has to
be done manually in the code without using the "merge" method.

In the case of the ManyToMany Team - Sponsers relationship however, the
mapping table entries are handled nicely by a "merge" operation.

It seems to be the ManyToOne and OneToMany relationships that developers
have to handle manually.
OneToOne relationships seem to work fine.

-sonavor


Johannes Michler-2 wrote:
> 
> Hi,
> @Chris: Yes this is what was my solution so far, to update the collections
> on add and remove manually. But this is quite ugly :-( Using @PostRemove
> or
> @PreRemove also didn't really help and caused ugly problems from time to
> time. For hibernate there is a special non-jpa-standard annotation (I
> think
> it could be "inverse", but I'm not quite sure and I couldn't find the site
> I
> read about this again) to handle this. If I understood you right, there is
> no such thing for EclipseLink :-(
> 
> @Joe: I think the Cascade.Remove doesn't really help. As mentioned, my
> database get's updated the right way. I couldn't see any effect on my
> issue
> when using the @cascade annotation unfortunately.
> 
> Any way, thanks for your help. I fear I'll have to do the updates of the
> collections manually :-(
> 
> -Johannes
> 
> 2009/7/13 <christopher.delahunt@xxxxxxxxxx>
> 
>> Hello Johannes,
>>
>>
>>
>> JPA does not maintain relationships for you, and instead requires
>> applications to maintain both sides of every relationship so that they
>> remain consistent with what is in the database.  What that means here is
>> that when you are deleting a Player, you also need to remove any
>> references
>> that might remain in the object model to the deleted player - in this
>> case
>> remove the player from the players set.  If not, Team will reference the
>> removed player until it gets refreshed.
>>
>>
>>
>> The application has the same issue when you add a player to a team - the
>> application must set both sides of the relationship, or the object model
>> may
>> not reflect what gets set in the database.
>>
>>
>>
>> Hope this helps.
>>
>> Chris
>>
>>
>> ----- Original Message -----
>> From: "Orgler" <orgler@xxxxxxxxx>
>> To: "Eclipselink-Users" <eclipselink-users@xxxxxxxxxxx>
>> Sent: Sunday, July 12, 2009 7:19:39 AM GMT -05:00 US/Canada Eastern
>> Subject: [eclipselink-users] Problems with ManyToOne and OneToMany
>>
>> Hi,
>>
>> I'm using eclipselink to do some persistence. I'm having the following
>> classes/tables:
>>
>> @Entity
>> public class Team{
>>     @OneToMany(mappedBy="team")
>>     public Set<Player> players;
>> }
>>
>> @Entity
>> public class Player{
>>     @ManyToOne
>>     public Team team;
>> }
>>
>> In my database there is a table team and a table player (with a
>> foreign-key-relationship to a team-ID).
>>
>> This works as far as I'm not deleting anything. But when calling
>> em.remove(myPlayer) the "players"-Set in "Team" is not automatically
>> updated. When closing my application and restarting it, everything works
>> fine. But without this restart the "players"-array seems to get cached
>> somewhere.
>>
>> Any idea on how to solve this? From time to time I'm getting errors that
>> there are unpersistet instances after such a remove operation. For
>> hibernate, I've read something about an "inverse"-annotation which could
>> (if
>> I understood it right) solve such a problem.
>>
>> Any help would be highly appreciated,
>>
>> best regards
>> -Johannes
>>
>>
>> _______________________________________________
>> eclipselink-users mailing list
>> eclipselink-users@xxxxxxxxxxx
>> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
>>
>>
> 
> _______________________________________________
> eclipselink-users mailing list
> eclipselink-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/eclipselink-users
> 
> 

-- 
View this message in context: http://www.nabble.com/Problems-with-ManyToOne-and-OneToMany-tp24447755p24492646.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top