【问题标题】:JPQL count Parent Objects on Multiple Children Match in OneToMany RelationshipJPQL 计算 OneToMany 关系中多个子匹配的父对象
【发布时间】:2020-01-30 00:23:11
【问题描述】:

在 JavaEE JPA Web 应用程序中,功能实体与患者实体具有双向多对一关系。我想编写一个查询来计算具有一个或多个匹配标准特征的患者数量。我使用 EclipseLink 作为 Persistence Provider。

例如,我想计算具有“variableName”=“Sex”和“variableData”=“Female”的特征以及“variableName”=“吸烟”和“variableData”=的另一个特征的患者数量'是的'。

如何编写 JPQL 查询来获取患者人数?

在第一个答案之后,我尝试了这个查询没有按预期给出任何结果。

public void querySmokingFemales(){
    String j = "select count(f.patient) from Feature f "
            + "where ((f.variableName=:name1 and f.variableData=:data1)"
            + " and "
            + " (f.variableName=:name2 and f.variableData=:data2))";
    Map m = new HashMap();
    m.put("name1", "sex");
    m.put("data1", "female");
    m.put("name2", "smoking");
    m.put("data2", "yes");
    count = getFacade().countByJpql(j, m);
}

患者实体如下。

@Entity
public class Patient implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "patient")
    private List<Feature> features;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }



    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Patient)) {
            return false;
        }
        Patient other = (Patient) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "entity.Patient[ id=" + id + " ]";
    }

    public String getName() {
        return name;
    }

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

    public List<Feature> getFeatures() {
        return features;
    }

    public void setFeatures(List<Feature> features) {
        this.features = features;
    }

}

这是特征实体。

@Entity
public class Feature implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String variableName;
    private String variableData;
    @ManyToOne
    private Patient patient;



    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Feature)) {
            return false;
        }
        Feature other = (Feature) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "entity.Feature[ id=" + id + " ]";
    }

    public String getVariableName() {
        return variableName;
    }

    public void setVariableName(String variableName) {
        this.variableName = variableName;
    }

    public String getVariableData() {
        return variableData;
    }

    public void setVariableData(String variableData) {
        this.variableData = variableData;
    }

    public Patient getPatient() {
        return patient;
    }

    public void setPatient(Patient patient) {
        this.patient = patient;
    }

}

【问题讨论】:

    标签: jpa eclipselink jpql


    【解决方案1】:

    对于单个功能计数您可以使用此

    select count(f.patient) from Feature f where f.variableName=:name and f.variableData:=data
    

    两个特征计数

    select count(distinct p) from Patient p, Feature f1, Feature f2 
    where 
      p.id=f1.patient.id and p.id=f2.patient.id and 
      f1.variableName=:name1 and f1.variableData:=data1 and 
      f2.variableName=:name2 and f2.variableData:=data2
    

    多个特征计数解决方案有点棘手。 org.springframework.data.jpa.domain.Specification可以使用

        public class PatientSpecifications {
          public static Specification<Patient> hasVariable(String name, String data) {
            return (root, query, builder) ->  {
                        Subquery<Fearure> subquery = query.subquery(Fearure.class);
                        Root<Fearure> feature = subquery.from(Fearure.class);
    
                        Predicate predicate1 = builder.equal(feature.get("patient").get("id"), root.get("id"));
    
                        Predicate predicate2 = builder.equal(feature.get("variableName"), name);
                        Predicate predicate3 = builder.equal(feature.get("variableData"), data);
    
                        subquery.select(operation).where(predicate1, predicate2, predicate3);
    
                        return builder.exists(subquery);
            }
          }
        }
    

    那么您的 PatientRepository 必须扩展 org.springframework.data.jpa.repository.JpaSpecificationExecutor&lt;Patient&gt;

    @Repository
    public interface PatientRepository 
        extends JpaRepository<Patient, Long>, JpaSpecificationExecutor<Patient> {
    
    }
    

    您的服务方式:

    @Service
    public class PatientService {    
    
       @Autowired
       PatientRepository patientRepository;
    
       //The larger map is, the more subqueries query would involve. Try to avoid large map
       public long countPatiens(Map<String, String> nameDataMap) {
             Specification<Patient> spec = null;
    
             for(Map.Entry<String, String> entry : nameDataMap.entrySet()) {
                Specification<Patient> tempSpec = PatientSpecifications.hasVariable(entry.getKey(), entry.getValue());
                if(spec != null)
                  spec = Specifications.where(spec).and(tempSpec);
                else spec = tempSpec;
    
             }
    
             Objects.requireNonNull(spec);
    
             return patientRepository.count(spec);        
        }
    }
    

    【讨论】:

    • 它给出了这个错误。编译问题 [从 Feature f 中选择 count(f.patient),其中 f.variableName=:name 和 f.variableData:=data]。 [72, 87] 状态字段路径“f.variableData:”无法解析为有效类型。 [90, 94] FROM 子句中未定义标识变量“数据”。
    • 对不起,这是一个类型。现在查询不计算记录。
    • 又是我的打字错误。它工作正常。谢谢。
    • 它适用于一个功能。但我需要搜索两个功能。如问题中所述,“性别”=“男性”和“吸烟”=“是”。
    • 您只需要两个特征患者计数还是多个特征患者计数?
    【解决方案2】:

    我们还为两个特征处理了相同的情况,在提取 ID 之后,我们使用了一个嵌套循环来计算公共计数的数量。这是资源密集型的,答案中的这两个功能查询有很大帮助。

    【讨论】:

      【解决方案3】:

      可能需要重新设计类结构以便查询更容易。

      【讨论】:

        猜你喜欢
        • 2016-09-03
        • 1970-01-01
        • 2014-07-30
        • 1970-01-01
        • 2011-04-21
        • 2013-05-07
        • 1970-01-01
        • 2020-01-01
        • 2017-07-07
        相关资源
        最近更新 更多