java - JPA Criteria Query - 如何在两个表上实现联接以在单个查询中获得所需的结果

标签 java hibernate jpa criteria-api

我有 2 个与数据库表映射的类。

复合主键类:

@Embeddable
public class Pk implements Serializable, Cloneable {

  @Column(name = "dataId")
  private String dataId;

  @Column(name = "occurrenceTime")
  private Timestamp occurrenceTime;

  public String getDataId() {
    return dataId;
  }

  public Pk setDataId(String dataId) {
    this.dataId = dataId;
    return this;
  }

  public Timestamp getOccurrenceTime() {
    return occurrenceTime;
  }

  public Pk setOccurrenceTime(Timestamp occurrenceTime) {
    this.occurrenceTime = occurrenceTime;
    return this;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Pk pk = (Pk) o;
    return Objects.equals(getDataId(), pk.getDataId()) &&
        Objects.equals(getOccurrenceTime(), pk.getOccurrenceTime());
  }

  @Override
  public int hashCode() {

    return Objects.hash(getDataId(), getOccurrenceTime());
  }
}

1:加载配置文件

@Entity
@Table(name = "energy")
public class LoadProfile implements Serializable, Cloneable {

  public LoadProfile() {
  }

  @EmbeddedId
  private Pk pk;

  @Column(name = "RECEIVE_TIME")
  private Timestamp reportingTime;

  @Column(name = "DATA1")
  private Double DATA1;


  @OneToOne
  @JoinColumns({
      @JoinColumn(name = "dataId", insertable = false, updatable = false, referencedColumnName = "dataId"),
      @JoinColumn(name = "occurrenceTime", insertable = false, updatable = false, referencedColumnName = "occurrenceTime")
  })
  private ForwardPower forwardPower;

  public Pk getPk() {
    return pk;
  }

  public LoadProfile setPk(Pk pk) {
    this.pk = pk;
    return this;
  }

  public Timestamp getReportingTime() {
    return reportingTime;
  }

  public LoadProfile setReportingTime(Timestamp reportingTime) {
    this.reportingTime = reportingTime;
    return this;
  }

  public Double getDATA1() {
    return DATA1;
  }

  public LoadProfile setDATA1(Double DATA1) {
    this.DATA1 = DATA1;
    return this;
  }

  public ForwardPower getForwardPower() {
    return forwardPower;
  }

  public LoadProfile setForwardPower(
      ForwardPower forwardPower) {
    this.forwardPower = forwardPower;
    return this;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    LoadProfile that = (LoadProfile) o;
    return Objects.equals(getPk(), that.getPk());
  }

  @Override
  public int hashCode() {

    return Objects.hash(getPk());
  }
}

2:前向功率

@Entity
@Table(name = "forward_power")
public class ForwardPower implements  Serializable, Cloneable  {

  public ForwardPower() {
  }

  @EmbeddedId
  private Pk pk;

  @Column(name = "RECEIVE_TIME")
  private Timestamp reportingTime;

  @Column(name = "DATA2")
  private Double DATA2;

  public Pk getPk() {
    return pk;
  }

  public ForwardPower setPk(Pk pk) {
    this.pk = pk;
    return this;
  }

  public Timestamp getReportingTime() {
    return reportingTime;
  }

  public ForwardPower setReportingTime(Timestamp reportingTime) {
    this.reportingTime = reportingTime;
    return this;
  }

  public Double getDATA2() {
    return DATA2;
  }

  public ForwardPower setDATA2(Double DATA2) {
    this.DATA2= DATA2;
    return this;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    ForwardPower that = (ForwardPower) o;
    return Objects.equals(getPk(), that.getPk());
  }

  @Override
  public int hashCode() {

    return Objects.hash(getPk());
  }
}

我想执行查询

Select * From energy e    
Left join forward_power fp    
on fp.dataId== e.dataId and fp.occurrenceTime == e.occurrenceTime    
where     e.occurrenceTime >= '2017-12-28 00:00:00'     
and       e.occurrenceTime <= '2018-01-02 00:00:00'    
Limit 1000;

我使用 JPA 标准查询在 java 中编写了一个等效的查询

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<LoadProfile> cq = cb.createQuery(LoadProfile.class);
Root<LoadProfile> loadProfileRoot = cq.from(LoadProfile.class);
Join<LoadProfile, ForwardPower> join = loadProfileRoot.join(LoadProfile_.forwardPower);
List<Predicate> conditions = new ArrayList();
conditions.add(cb.equal(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.dataId), join.get(
    ForwardPower_.pk).get(Pk_.dataId)));
conditions.add(cb.equal(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.occurrenceTime),
    join.get(ForwardPower_.pk).get(Pk_.occurrenceTime)));

conditions.add(
    cb.greaterThanOrEqualTo(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.occurrenceTime),
        config.getDataStartTime()));
conditions.add(
    cb.lessThanOrEqualTo(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.occurrenceTime),
        config.getDataEndTime()));

cq.select(loadProfileRoot);
cq.where(conditions.toArray(new Predicate[]{}));
Query query = session.createQuery(cq);
List list = query.setFirstResult(0).setMaxResults(1000).getResultList();

我设置了选项 hibernate.show_sql = true。 现在该查询给出了我确切的 1000 个所需结果。 当我看到上面代码由 ORM 生成的 hibernate 查询时。 ORM 为能源表创建 1 个查询,为前向功率表创建 1000 个查询,这会导致性能问题,并且查询花费太多时间来获取 1000 条记录,大约需要 55 - 60 秒。

我如何创建一个条件查询,以便 ORM 为该代码生成 1 个查询?

提前致谢。

最佳答案

您可以在您的关系上添加获取类型的急切指令,并且 ForwardPower 将通过任何 LoadProfile.find 使用 LoadProfile 加载

  @OneToOne(fetch=FetchType.EAGER)
  @JoinColumns({
      @JoinColumn(name = "dataId", insertable = false, updatable = false, referencedColumnName = "dataId"),
      @JoinColumn(name = "occurrenceTime", insertable = false, updatable = false, referencedColumnName = "occurrenceTime")
  })
  private ForwardPower forwardPower;

或者您可以在查询中添加获取指令。 我不太熟悉,但大概是这样的

//instead of loadProfileRoot.join(LoadProfile_.forwardPower)
Join<LoadProfile, ForwardPower> join = (Join<LoadProfile, ForwardPower>) loadProfileRoot.fetch(LoadProfile_.forwardPower);

参见JPA 2 Criteria Fetch Path Navigation有关使用 CriteriaBuilder 获取的更多信息。

关于java - JPA Criteria Query - 如何在两个表上实现联接以在单个查询中获得所需的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48061564/

相关文章:

java - JPA "The multi-part identifier ... could not be bound"

hibernate - 使用 Hibernate 将实体映射到表时出现 BeanCreationException

java - Hibernate (JPA) 和 PostgreSQL 的查询问题

java - 使用具有 mysql 数据库的 IntelliJ IDEA 创建 Jar 文件

java - 异常发现得太早

hibernate - 如何取消 hibernate 查询

java - 使用可连接的两个表的 JPA 映射以及何时使用可连接

java - 使用 java 解析多级 XML 文件(DOM 解析器)

java - 使用 get 时,int[][] 类型的 vector 不会替换 int[][]

java - 连接 Amazon RDS 上的顶级 PostgreSQL 9.3.1 时 Hibernate/JDBC 挂起