1

I'm using JPA / EclipseLink 2.5.2 and want to add an additional where clause to my ManyToMany Mapping. The SQL Code would look like this:

SELECT * FROM Friends f
INNER JOIN User u ON f.friend_id = u.id 
WHERE f.verified=1; 

So I managed to do the JoinMapping with:

@Entity
@NamedQueries({
    @NamedQuery(name="User.findAll", query="SELECT u FROM User u"),
    @NamedQuery(name="User.login", query="SELECT a FROM User a WHERE a.username = :name and a.password = :password"),
    @NamedQuery(name="User.findId", query="SELECT a FROM User a WHERE a.id = :id"),
    @NamedQuery(name="User.findName", query="SELECT a FROM User a WHERE a.username = :name")

})
public class User implements Serializable {
...
@ManyToMany
@JoinTable(
    name="Friends"
    , joinColumns={
        @JoinColumn(name="user_id")
        }
    , inverseJoinColumns={
        @JoinColumn(name="friend_id")
        }
    )
List<User> friends;
}

But I dont know how to add the WHERE f.verified=1

How can this be realized?

Thanks in advance, Cassidy

Allstar
  • 13
  • 1
  • 5
  • You only defined the join between the tables. But how do you retrieve the entities? do you use NamedQuery or CriteriaQuery? – Jama Djafarov Dec 10 '14 at 21:49
  • Possible dup http://stackoverflow.com/questions/8582452/jpa-manytomany-how-can-jointable-have-a-property – Shmil The Cat Dec 10 '14 at 21:50
  • The one User I retrieve via a NamedQuery and with the ManyToMany Mapping I want to retrieve the List friends which contains all friends from the JoinTable "Friends" where "Friends.verified=1" – Allstar Dec 10 '14 at 22:09
  • Not really a duplicate as this question is mainly about filtering the list. However you do also have the problem discussed in that question which is that the 'verified' flag is on the join table and therefore you do need to scrap the many-many-many and create an entity say 'Friendship' which has this additional property. You then create a one-to-many User > Friendship. Which is what this other question is discussing. – Alan Hay Dec 10 '14 at 22:13
  • @NamedQuery(name = "Users.findXXX", query = "SELECT u FROM User u, IN(u.friends) f where f.verified=1"); Or you can use regular join: "SELECT u FROM User u Join u.friends f where f.verified=1" – Jama Djafarov Dec 10 '14 at 22:17
  • But when i use @NamedQuery it is not possible to associate it with the member List friends, is it? – Allstar Dec 10 '14 at 22:25
  • this should work: "SELECT u FROM User u Join u.friends f where f.verified=1 AND u.username = :name" – Jama Djafarov Dec 10 '14 at 22:28

2 Answers2

1

As advised in my comment you cannot use @ManyToMany as you require an additional column in the join table to indicate whether verified or not.

You then need to use a @OneToMany with an additional Entity, say Friendship. We can use the verified column as a discriminator and use a simple class hierarchy to distinguish between Unconfirmed and Confirmed friends.

This will then look something like the below (haven't tested it fully).

Note, I tested this with Hibernate but there is an issue so will need to look at again. These posts suggests the issue may be Hibernate specific:

So may be worth trying with EclipseLink.

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @OneToMany(mappedBy = "user")
    private Set<ConfirmedFriendship> confirmedFriendships;

    @OneToMany(mappedBy = "user")
    private Set<UnconfirmedFriendship> unconfirmedFriendships;

    public List<User> getConfirmedFriends() {
        return getFriends(confirmedFriendships);
    }

    public List<User> getUnconfirmedFriends() {
        return getFriends(unconfirmedFriendships);
    }

    private List<User> getFriends(Set<?  extends Friendship> friendships){
        List<User> friends = new ArrayList<User>();

        for(Friendship friendship : friendships) {
            friends.add(friendship.getFriend());
        }

        return friends;
    }
}

Base Entity for Friendship:

@Entity
@Table(name = "friendships")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "verified")
public abstract class Friendship {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @JoinColumn(name = "friend_id")
    private User friend;

    @Column(name = "verified")
    private boolean verified;

    public int getId() {
        return id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public User getFriend() {
        return friend;
    }

    public void setFriend(User friend) {
        this.friend = friend;
    }

    public boolean isVerified() {
        return verified;
    }

    public void setVerified(boolean verified) {
        this.verified = verified;
    }
}

Two subclassses which use the verified column as discriminator:

@Entity
@DiscriminatorValue(value = "1")
public class ConfirmedFriendship extends Friendship {

}

@Entity
@DiscriminatorValue(value = "0")
public class UnconfirmedFriendship extends Friendship {

}
Community
  • 1
  • 1
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • This is exactly what I was looking for! Thank you very much for your effort! – Allstar Dec 11 '14 at 10:48
  • Out of interest, and given the issue I outlined with Hibernate, does this work as is in EclipseLInk? – Alan Hay Dec 11 '14 at 11:09
  • Yes, as far as I have tested it yet, it works fine with EclipseLink. – Allstar Dec 11 '14 at 11:52
  • @Allstar I know this is old but can you please provide a working example? I cannot make it work (jpa2.1, eclipseLink 2.6.4). Everything is saved correctly in db, yet the `friendships` lists in user are not loaded. – Odys Nov 30 '16 at 22:43
0

I dont think there is JPA standard to include the "Where" option. Personally I use NamedQuery to retrieve the entities. You can also use CriteriaQuery.

Well, In eclipseLink, you can use @AdditionalCriteria("this.verified=1")

Community
  • 1
  • 1
Jama Djafarov
  • 358
  • 3
  • 11
  • 1
    When I use @AdditionalCriteria("this.verified=1") I need to create a new Entity doesn't I? Is it possible to connect a NamedQuery directly with a member variable? So i can still use the "List friends" member in my User Class? – Allstar Dec 10 '14 at 22:11
  • @AdditionalCriteria("this.verified=1") is entity level. I would suggest to use NamedQuery: "SELECT u FROM User u Join u.friends f where f.verified=1" – Jama Djafarov Dec 10 '14 at 22:24