JPA Criteria Query - 如何在两个表上实现 Join 以在单个查询中获得所需的结果
JPA Criteria Query - How to implement Join on two tables to get desired result in single Query
我有 2 个 类 映射到数据库表。
复合主键Class:
@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 通过上面的代码生成的休眠查询时。
ORM 创建 1 个能量查询 table 和 1000 个前向功率查询 table,这会导致性能问题和查询花费太多时间 aproximately 55 - 60 秒来获取 1000 条记录。
我如何创建条件查询以便 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);
有关使用 CriteriaBuilder 提取的更多信息,请参阅 JPA 2 Criteria Fetch Path Navigation。
我有 2 个 类 映射到数据库表。
复合主键Class:
@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 通过上面的代码生成的休眠查询时。 ORM 创建 1 个能量查询 table 和 1000 个前向功率查询 table,这会导致性能问题和查询花费太多时间 aproximately 55 - 60 秒来获取 1000 条记录。
我如何创建条件查询以便 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);
有关使用 CriteriaBuilder 提取的更多信息,请参阅 JPA 2 Criteria Fetch Path Navigation。