views:

1093

answers:

6

It's quite some time that I'm trying to figure out this problem and from googling around many people have similar problems.

I'm trying to model a User in a Social Network, using Hibernate, and what is more basic to a social network than to map a friendship relation? Every user in the system should have a list of it's friends and I thought that this might be an incredibly easy task (just use a ManyToMany relation, right?). So I went on to try the following:

@Entity
@Table(name="users")
public class User {
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   @Column(name="userid")
   protected Long id = null;
   @ManyToMany
   protected List<User> friends = null
}

The problem now is that it tells me I use ManyToMany wrongly by having no clear distinction between friend and befriended. So far so good, I get the error, but how can I do what I want?

Any idea? I've reached the end of my wisdom.

+1  A: 

The ManyToMany annotation has a mappedBy parameter. I guess something like

@ManyToMany(mappedBy = "friends")

might work. In any case, see the docs.

Edit:

It seems as if a many to many relationship needs to be accessible from both ends. Currently yours is only accessible from one end. Maybe you should add the other relation as well:

@Entity
@Table(name="users")
public class User {
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   @Column(name="userid")
   protected Long id = null;
   @ManyToMany
   protected List<User> friends = null;
   @ManyToMany(mappedBy = "friends")
   protected List<User> befriended = null;
}

If that doesn't work you can always introduce a separate class representing the relation between two users, and let every user have a collection of instances of this class.

Ole
A: 

Good point, and in fact I tried that. Problem is that I then get a complaint about the mappedBy attribute being set on both sides of the relationship, which is true, but invalid. I was wondering wether a simple @ManyToMany with some clever custom query to fetch the friends might be a solution: The ManyToMany would generate a join table with user_id and friend_id, but the query would match either of the fields, returning all users where that match either the friend_id or the user_id that is. Any ideas?

cdecker
A: 

That of course would be a good fix, yet I'm not yet completely sold. Basically to get the friends I'd have to merge the two collections, which is quite unsatisfactory.

Is there absolutely no way to create an idempotent, reflexive relation in hibernate?

cdecker
A: 

The fact about Many to Many is that it needs a little more configuration, because its imperative that Hibernate generates a relation table. Try this:


@Entity
@Table(name="users")
public class User {
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   @Column(name="userid")
   protected Long id = null;
   @ManyToMany
   @JoinTable(name = "user_friends", 
       joinColumns = @JoinColumn(name = "user_id"), 
       inverseJoinColumns = @JoinColumn(name = "friend_id"))
   protected List friends = null;
   @ManyToMany(mappedBy = "friends")
   protected List befriended = null;
}

Hope it works =)

EDIT: Also, be very careful with fetch types... you can enter an user fetching out-of-control loop and get all the DB.

Juan Manuel
A: 

I had the same problem today as well.

@Entity
@Table(name="users")
public class User {
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   @Column(name="userid")
   protected Long id = null;
   @ManyToMany
   protected List<User> friends = null
}

Should be ok, I used a similar structure. However I got lazy loading exceptions and when setting the fetchType to EAGER, I got complaints about recursive initialisation of bags.

How I fixed my problem: In the query you use to fetch the 'user', do something like:

from User as u left join fetch u.friends

This will initialise the list of friends for that entity. Be warned though that it doesn't initialise any friends from those friends. That is a good thing actually.

Matthias van der Vlies
A: 

Ok, but now, how can you remove friend this way? For instance, using Hibernate Template? Or any other way?

Artem