【问题标题】:How to write query(include subquery and exists) using JPA Criteria Builder如何使用 JPA Criteria Builder 编写查询(包括子查询和存在)
【发布时间】:2013-10-07 10:49:43
【问题描述】:

正在努力使用 JPA 编写以下查询。

Oracle 查询:

Select * from table1 s
where exists (Select 1 from table2 p
              INNER JOIN table3 a ON a.table2_id = p.id
              WHERE a.id = s.table3_id
              AND p.name = 'Test');

另外,您想指出在 JPA 中编写复杂查询的任何好的教程吗?

【问题讨论】:

    标签: hibernate jpa jpa-2.0 criteria


    【解决方案1】:

    我将回答使用JpaRepository、JpaSpecificationExecutor、CriteriaQuery、CriteriaBuilder的简单汽车广告域(advert、brand、model)的例子:

    • 品牌 [一对多] 模型
    • 模型 [一对多] 广告

    实体:

    @Entity
    public class Brand {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      private String name;
      @OneToMany(mappedBy = "brand", fetch = FetchType.EAGER)
      private List<Model> models;
    }
    
    @Entity
    public class Model {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      private String name;
      @ManyToOne
      @JoinColumn(name = "brand_id")
      private Brand brand;
    }
    
    @Entity
    public class Advert {
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private Long id;
      @ManyToOne
      @JoinColumn(name = "model_id")
      private Model model;
      private int year;
      private int price;
    }
    

    存储库:

    public interface AdvertRepository
      extends JpaRepository<Advert, Long>, JpaSpecificationExecutor<Advert> {
    }
    

    规格:

    public class AdvertSpecification implements Specification<Advert> {
      private Long brandId;
    
      public AdvertSpecification(Long brandId) {
        this.brandId = brandId;
      }
    
      @Override
      public Predicate toPredicate(Root<Advert> root,
                                   CriteriaQuery<?> query,
                                   CriteriaBuilder builder) {
    
        Subquery<Model> subQuery = query.subquery(Model.class);
        Root<Model> subRoot = subQuery.from(Model.class);
    
        Predicate modelPredicate = builder.equal(root.get("model"), subRoot.get("id"));
    
        Brand brand = new Brand();
        brand.setId(brandId);
        Predicate brandPredicate = builder.equal(subRoot.get("brand"), brand);
    
        subQuery.select(subRoot).where(modelPredicate, brandPredicate);
        return builder.exists(subQuery);
      }
    }
    

    效果就是这个Hibernate SQL:

    select advert0_.id as id1_0_,
           advert0_.model_id as model_id5_0_,
           advert0_.price as price3_0_,
           advert0_.year as year4_0_
    from advert advert0_
    where exists (select model1_.id from model model1_
                  where advert0_.model_id=model1_.id
                  and model1_.brand_id=?)
    

    【讨论】:

    • 嗨,这是一个很好的例子。 (当您执行“brand.setId(”...) 时,我找不到“criteria.getValue()”的来源。它应该是“this.brandId”(成员变量)吗?
    • 哇,我刚刚将它转换为使用强类型。 (我的 org.springframework.data.jpa.domain.Specification 使用通过在 MyObject_ 类中创建“import javax.persistence.metamodel.SingularAttribute;”和@javax.persistence.metamodel.StaticMetamodel(MyObject.class) 实现的强类型。Youch !这是很多巫术来让它工作。感谢这个例子,它让我离开了地面!
    【解决方案2】:

    您可以使用 JPA 查询或 HQL 而不是 Criteria 构建器更简单:

    SELECT e1 from Entity1 as e1 
    where exists
    (select e2 from Entity2 as e2 join e2.e3 as ent3
    where ent3.id=e1.id and e2.name='Test')
    

    【讨论】:

    • 我了解,但要求是使用标准生成器来完成 :(
    • Criteria builder 有其自身的局限性,因此最好根据情况将其与查询结合使用。不要试图用螺丝刀钉钉子,它会转化为难。
    猜你喜欢
    • 2017-07-20
    • 1970-01-01
    • 2020-12-07
    • 2010-12-10
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    • 2014-09-04
    相关资源
    最近更新 更多