EDITOR: Well, I'm quite a mess here, but hope this time I'm closer to the correct answer.
Consider (identifiers are automatically generated as 1 for John, etc.):
INSERT INTO some_user (name) VALUES ('John'); INSERT INTO some_user (name) VALUES ('Ariel'); INSERT INTO some_user (name) VALUES ('Brian'); INSERT INTO some_user (name) VALUES ('Kelly'); INSERT INTO some_user (name) VALUES ('Tom'); INSERT INTO some_user (name) VALUES ('Sonya'); INSERT INTO product (owner_id,name) VALUES (1,'Nokia 3310'); INSERT INTO product (owner_id,name) VALUES (2,'Sony Xperia Aqua'); INSERT INTO product (owner_id,name) VALUES (3,'IPhone 4S'); INSERT INTO product (owner_id,name) VALUES (1,'Xiaomi MI5'); INSERT INTO product (owner_id,name) VALUES (3,'Samsung Galaxy S7'); INSERT INTO product (owner_id,name) VALUES (3,'Sony Xperia Z3'); INSERT INTO following_relationship (follower_id, owner_id) VALUES (4,1); INSERT INTO following_relationship (follower_id, owner_id) VALUES (5,1); INSERT INTO following_relationship (follower_id, owner_id) VALUES (4,2); INSERT INTO following_relationship (follower_id, owner_id) VALUES (6,2); INSERT INTO following_relationship (follower_id, owner_id) VALUES (6,3); INSERT INTO following_relationship (follower_id, owner_id) VALUES (1,3);
Based on the simplified version of the entities you provided and SomeUser Entity, for example:
@Entity public class FollowingRelationship { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER) @JoinColumn(name = "owner_id") SomeUser owner; @ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER) @JoinColumn(name = "follower_id") SomeUser follower; ... @Entity public class Product { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @ManyToOne() @JoinColumn(name = "owner_id") private SomeUser owner; @Column private String name; ... @Entity public class SomeUser { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @Column private String name; @OneToMany(mappedBy = "owner") private Set<Product> products = new HashSet<Product>(); @OneToMany(mappedBy = "owner") private Set<FollowingRelationship> ownedRelationships = new HashSet<FollowingRelationship>(); @OneToMany(mappedBy = "follower") private Set<FollowingRelationship> followedRelationships = new HashSet<FollowingRelationship>();
I created a spec like:
public static Specification<Product> joinTest(SomeUser input) { return new Specification<Product>() { public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Join<Product,SomeUser> userProd = root.join("owner"); Join<FollowingRelationship,Product> prodRelation = userProd.join("ownedRelationships"); return cb.equal(prodRelation.get("follower"), input); } }; }
And now, when we make a request like:
SomeUser someUser = someUserRepository.findOne(Specifications.where(ProductSpecifications.userHasName("Kelly"))); List<Product> thatProducts = productRepository.findAll(Specifications.where(ProductSpecifications.joinTest(someUser))); System.out.println(thatProducts.toString());
We get:
[Product [id=1, name=Nokia 3310], Product [id=4, name=Xiaomi MI5], Product [id=2, name=Sony Xperia Aqua]]
And this, in my opinion, is equivalent to: "get all the products from all users to which another user is subject" - get the products of all users that Kelly adheres to.