Spring Data Join 规范

Posted

技术标签:

【中文标题】Spring Data Join 规范【英文标题】:Spring Data Join with Specifications 【发布时间】:2016-11-05 04:55:59 【问题描述】:

我正在尝试转换这个原始 sql 查询:

select product.* from following_relationship
join product on following_relationship.following=product.owner_id
where following_relationship.owner=input 

在 Spring Data 规范中,我认为到目前为止我的问题在于加入这些表。

这是我目前在规范中的转换:

protected Specification<Product> test(final User user)
   return new Specification<Product>() 
       @Override
       public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder cb) 
           Join<FollowingRelationship,Product> pfJoin = query.from(FollowingRelationship.class).join("following");
           pfJoin.on(cb.equal(pfJoin.get("following"),"owner"));
           return  query.where(cb.equal(pfJoin.get("following"),user)).getGroupRestriction();

       
   ;

我得到了这个例外:

Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessA
piUsageException: org.hibernate.hql.internal.ast.InvalidWithClauseException: with clause can only reference columns in the driving table 

我想补充一点,我是 Spring 框架的新手,例如这是我在 Spring 上的第一个应用程序,所以对于新手问题我深表歉意 ;)

编辑:添加实体 Product、FollowingRelationShip

Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "json_id_prop")
public class FollowingRelationship extends BaseEntity 

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "OWNER", referencedColumnName = "uuid")
    private User owner;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "FOLLOWING", referencedColumnName = "uuid")
    private User following;

    public User getOwner() 
        return owner;
    

    public void setOwner(User owner) 
        this.owner = owner;
    

    public User getFollowing() 
        return following;
    

    public void setFollowing(User following) 
        this.following = following;
    



@Entity
@Table(name = "product")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "json_id_prop")
public class Product extends BaseEntity 

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "OWNER_ID", referencedColumnName = "uuid")
    private User owner;
    @NotNull
    private String name;
    @NotNull
    private String description;
    @NotNull
    private String price;
    @NotNull
    private String brand;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getDescription() 
        return description;
    

    public void setDescription(String description) 
        this.description = description;
    

    public String getPrice() 
        return price;
    

    public void setPrice(String price) 
        this.price = price;
    

    public String getBrand() 
        return brand;
    

    public void setBrand(String brand) 
        this.brand = brand;
    

    public User getOwner() 
        return owner;
    

    public void setOwner(User owner) 
        this.owner = owner;
    



Product 和 FollowRelationShip 实体没有任何明确的关系,因此加入我的实现 about。我想要实现的是从 Spring 数据规范中另一个用户遵循的所有用户获取所有产品。

【问题讨论】:

应该pfJoin.on(cb.equal(pfJoin.get("following"),"owner"));pfJoin.on(cb.equal(pfJoin.get("following"),"owner_id")); 吗? 这是第 3 方包裹挡路的情况。. @Galz 是的,owner_id 是连接列的名称,它引用了所有者 uuid 字段(这也是实体的 ID)。顺便说一句,我已经尝试过 owner_id 和 owner 但没有工作 @murielK - 你能分享一下 BaseEntity 吗? 【参考方案1】:

编辑:好的,我在这里做得很乱,但我希望这次我更接近正确答案。

考虑(ID 是自动生成的,例如 John 等的 1):

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);

基于您提供的实体的简化版本,以及 SomeUser 实体,例如:

@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>();

我创建了如下规范:

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);
        
    ;

现在,我们可以执行查询:

SomeUser someUser = someUserRepository.findOne(Specification.where(ProductSpecifications.userHasName("Kelly")));
List<Product> thatProducts = productRepository.findAll(Specification.where(ProductSpecifications.joinTest(someUser)));
System.out.println(thatProducts.toString());

我们得到:

[Product [id=1, name=Nokia 3310], Product [id=4, name=Xiaomi MI5], Product [id=2, name=Sony Xperia Aqua]]

在我看来,这相当于:“获取其他用户关注的所有用户的所有产品”- 获取 Kelly 关注的所有用户的产品。

【讨论】:

谢谢,我一回到家就会这样做。请 Product 和 FollowRelationship 没有任何关系,它们唯一的共同点是用户,因此每个产品都有它的所有者(用户),其中 FollowRelationship 具有以下字段和关注者字段(它们都是用户类型或实体)。因此,我试图获取其他特定用户正在关注的所有用户的所有产品。 这就是为什么我需要在 FollowRelationship 实体中加入产品实体的原因,其中 FollowRelationship.following = product.owner 好的,我会改写答案,但请在你回家后给我看实体。当你现在解释的时候我明白了基本的想法,但是当我看到它时会容易得多。 Helle @patrykos91 你有机会看看我更新的帖子吗?

以上是关于Spring Data Join 规范的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data 'Left Join Fetch' 查询返回 null

带有分页的 Spring-Data FETCH JOIN 不起作用

具有分页的Spring-Data FETCH JOIN不起作用

如何在 spring data jpa 中使用预测和规范?

Spring Data JPA方法定义规范

Spring Data JPA:创建规范查询获取连接