HQL 查询需要很长时间才能执行
HQL query takes to long to execute
我知道要冬眠。
我有一个sql声明
SELECT VERSION_ID,D_BEGIN,D_END,
(SELECT NAME FROM zvit where ZVIT_ID=version.ZVIT_ID) as name
FROM version
where VERSION_ID in
(SELECT term.VERSION_ID FROM term
where term.PERIOD_MONTH= :periodMonth and term.PERIOD_TYPE= :periodType and term.PERIOD_YEAR= :periodYear )
order by 1 ;
我尝试用 createCriteria 和 HQL 来实现它。
DetachedCriteria subQuery = DetachedCriteria.forClass(TermData.class)
.add(Restrictions.eq("periodmonth", periodMonth))
.add(Restrictions.eq("periodtype", periodType))
.add(Restrictions.eq("periodyear", periodYear))
.setProjection(Projections.projectionList()
.add(Projections.property("versionId.versionId"))
);
Criteria versionCriteria = session.createCriteria(VersionData.class)
.addOrder(Order.asc("versionId"))
.add(Subqueries.propertyIn("versionId", subQuery))
.createAlias("zvitId", "zvitId", org.hibernate.sql.JoinType.INNER_JOIN)
.setProjection(Projections.projectionList()
.add(Projections.property("versionId"))
.add(Projections.property("dbegin"))
.add(Projections.property("dend"))
.add(Projections.property("zvitId.name"), "name")
);
总部:
Query query = session.createQuery(""
+ "from VersionData as version "
// + "inner join version.zvitId as zvit "
+ "where version.versionId in "
+ "(select term.versionId from TermData as term "
+ "where term.periodmonth= :periodmonth and term.periodtype= :periodtype and term.periodyear= :periodyear)");
问题是这个 HQL 的执行时间要长 10 倍。并做了很多不必要的查询。我试着用注释字符串来做,它有所改进,但仍然比 createCriteria 查询长 5 倍,此外我不能做这个转换
List<VersionData> queryResult = query.list();
当我得到 VersionData 和 ZvitData 对象时。这似乎不是问题,但我仍然不明白为什么我不只收到 VersionData 对象。
但是还有一个更重要的问题是有什么方法可以改进这个 HQL 语句,使其与 mysql 或 createCriteria 查询同时执行。
版本数据定义
@Entity
@Table(name = "version")
public class VersionData implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 7355281418627668744L;
@Id
@Column(name="VERSION_ID")
private String versionId;
/**
* user dbegin
*/
@Column(name = "D_BEGIN")
@Type(type = "date")
private Date dbegin;
/** user dend */
@Column(name = "D_END")
@Type(type = "date")
private Date dend;
/**
* user zvitId
*/
@ManyToOne
@JoinColumn(name="ZVIT_ID")
private ZvitData zvitId;
@OneToMany(targetEntity=TermData.class, mappedBy = "versionId", cascade=javax.persistence.CascadeType.ALL, fetch=FetchType.LAZY)
private List<TermData> terms;
//getters, setters, hashcode, equals
}
好吧,使用您发布的标准查询,您只获取您需要的字段,而使用您的 HQL 查询,您获取整个 Hibernate 托管实体 (VersionData)。根据此实体的配置方式,您可能会看到其他查询被触发以填充此实体的字段。
您也可以使用 HQL 执行与标准相同的操作,在我看来,它比使用标准更易于理解,因此更易于维护。只需使用适当的构造函数为您希望获取的字段定义一个 java class MyClass,然后调用一个类似于以下内容的 HQL 查询:
"select new path.to.myclass.MyClass ( "
+ " alias1.field1, "
+ " alias2.field2, "
+ " ... "
其中别名是您为 HQL 查询中的实体指定的名称(例如,version 是您编写的查询中 VersionData 的别名)。没有预测,没有额外的抽象,只有一个 SQL 样的查询。
与其定义一个新的 class 来保存从 HQL 查询返回的字段(我觉得这样更方便),不如直接获取字段,然后处理返回的结果,如 link 下面:
How to retrieve only certain fields of an entity in JPQL or HQL? What is the equivalent of ResultSet in JPQL or HQL?
我知道要冬眠。 我有一个sql声明
SELECT VERSION_ID,D_BEGIN,D_END,
(SELECT NAME FROM zvit where ZVIT_ID=version.ZVIT_ID) as name
FROM version
where VERSION_ID in
(SELECT term.VERSION_ID FROM term
where term.PERIOD_MONTH= :periodMonth and term.PERIOD_TYPE= :periodType and term.PERIOD_YEAR= :periodYear )
order by 1 ;
我尝试用 createCriteria 和 HQL 来实现它。
DetachedCriteria subQuery = DetachedCriteria.forClass(TermData.class)
.add(Restrictions.eq("periodmonth", periodMonth))
.add(Restrictions.eq("periodtype", periodType))
.add(Restrictions.eq("periodyear", periodYear))
.setProjection(Projections.projectionList()
.add(Projections.property("versionId.versionId"))
);
Criteria versionCriteria = session.createCriteria(VersionData.class)
.addOrder(Order.asc("versionId"))
.add(Subqueries.propertyIn("versionId", subQuery))
.createAlias("zvitId", "zvitId", org.hibernate.sql.JoinType.INNER_JOIN)
.setProjection(Projections.projectionList()
.add(Projections.property("versionId"))
.add(Projections.property("dbegin"))
.add(Projections.property("dend"))
.add(Projections.property("zvitId.name"), "name")
);
总部:
Query query = session.createQuery(""
+ "from VersionData as version "
// + "inner join version.zvitId as zvit "
+ "where version.versionId in "
+ "(select term.versionId from TermData as term "
+ "where term.periodmonth= :periodmonth and term.periodtype= :periodtype and term.periodyear= :periodyear)");
问题是这个 HQL 的执行时间要长 10 倍。并做了很多不必要的查询。我试着用注释字符串来做,它有所改进,但仍然比 createCriteria 查询长 5 倍,此外我不能做这个转换
List<VersionData> queryResult = query.list();
当我得到 VersionData 和 ZvitData 对象时。这似乎不是问题,但我仍然不明白为什么我不只收到 VersionData 对象。
但是还有一个更重要的问题是有什么方法可以改进这个 HQL 语句,使其与 mysql 或 createCriteria 查询同时执行。
版本数据定义
@Entity
@Table(name = "version")
public class VersionData implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 7355281418627668744L;
@Id
@Column(name="VERSION_ID")
private String versionId;
/**
* user dbegin
*/
@Column(name = "D_BEGIN")
@Type(type = "date")
private Date dbegin;
/** user dend */
@Column(name = "D_END")
@Type(type = "date")
private Date dend;
/**
* user zvitId
*/
@ManyToOne
@JoinColumn(name="ZVIT_ID")
private ZvitData zvitId;
@OneToMany(targetEntity=TermData.class, mappedBy = "versionId", cascade=javax.persistence.CascadeType.ALL, fetch=FetchType.LAZY)
private List<TermData> terms;
//getters, setters, hashcode, equals
}
好吧,使用您发布的标准查询,您只获取您需要的字段,而使用您的 HQL 查询,您获取整个 Hibernate 托管实体 (VersionData)。根据此实体的配置方式,您可能会看到其他查询被触发以填充此实体的字段。
您也可以使用 HQL 执行与标准相同的操作,在我看来,它比使用标准更易于理解,因此更易于维护。只需使用适当的构造函数为您希望获取的字段定义一个 java class MyClass,然后调用一个类似于以下内容的 HQL 查询:
"select new path.to.myclass.MyClass ( "
+ " alias1.field1, "
+ " alias2.field2, "
+ " ... "
其中别名是您为 HQL 查询中的实体指定的名称(例如,version 是您编写的查询中 VersionData 的别名)。没有预测,没有额外的抽象,只有一个 SQL 样的查询。
与其定义一个新的 class 来保存从 HQL 查询返回的字段(我觉得这样更方便),不如直接获取字段,然后处理返回的结果,如 link 下面:
How to retrieve only certain fields of an entity in JPQL or HQL? What is the equivalent of ResultSet in JPQL or HQL?