使用休眠实现联合查询有哪些替代方法?我知道hibernate目前不支持联合查询,现在我看到的唯一方法是使用视图表。
另一种选择是使用普通的 jdbc,但这样我会丢失我所有的示例/条件查询好东西,以及休眠对表/列执行的休眠映射验证。
你可以使用id in (select id from ...) or id in (select id from ...)
例如,而不是非工作
from Person p where p.name="Joe"
union
from Person p join p.children c where c.name="Joe"
你可以做
from Person p
where p.id in (select p1.id from Person p1 where p1.name="Joe")
or p.id in (select p2.id from Person p2 join p2.children c where c.name="Joe");
不过,至少使用 MySQL,您稍后会遇到性能问题。有时在两个查询上做一个穷人的连接会更容易:
// use set for uniqueness
Set<Person> people = new HashSet<Person>((List<Person>) query1.list());
people.addAll((List<Person>) query2.list());
return new ArrayList<Person>(people);
做两个简单的查询通常比做一个复杂的查询要好。
编辑:
举个例子,这里是来自 subselect 解决方案的 MySQL 查询的 EXPLAIN 输出:
mysql> explain
select p.* from PERSON p
where p.id in (select p1.id from PERSON p1 where p1.name = "Joe")
or p.id in (select p2.id from PERSON p2
join CHILDREN c on p2.id = c.parent where c.name="Joe") \G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: a
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 247554
Extra: Using where
*************************** 2. row ***************************
id: 3
select_type: DEPENDENT SUBQUERY
table: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: Impossible WHERE noticed after reading const tables
*************************** 3. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: a1
type: unique_subquery
possible_keys: PRIMARY,name,sortname
key: PRIMARY
key_len: 4
ref: func
rows: 1
Extra: Using where
3 rows in set (0.00 sec)
最重要的是,1. row 不使用任何索引并考虑 200k+ 行。坏的!此查询的执行耗时 0.7 秒,其中两个子查询都以毫秒为单位。
使用视图。可以使用实体名称将相同的类映射到不同的表/视图,因此您甚至不会有太多重复。在那里,做到这一点,工作正常。
普通 JDBC 有另一个隐藏的问题:它不知道 Hibernate 会话缓存,因此如果某些内容被缓存到事务结束并且没有从 Hibernate 会话中刷新,则 JDBC 查询将找不到它。有时可能非常令人费解。
我必须同意弗拉基米尔的观点。我也研究过在 HQL 中使用 UNION 并且找不到解决方法。奇怪的是,我可以发现(在 Hibernate 常见问题解答中)UNION 不受支持,与 UNION 有关的错误报告标记为“已修复”,人们的新闻组说这些语句将在 UNION 被截断,以及其他新闻组的人报告它有效很好......经过一天的磨合后,我最终将我的 HQL 移植回纯 SQL,但在数据库中的视图中执行此操作将是一个不错的选择。在我的例子中,部分查询是动态生成的,所以我不得不在代码中构建 SQL。
我有一个针对 HQL 中的 union 的关键场景(我为此付出了很多努力)的解决方案。
例如,而不是不工作: -
select i , j from A a , (select i , j from B union select i , j from C) d where a.i = d.i
或者
select i , j from A a JOIN (select i , j from B union select i , j from C) d on a.i = d.i
你可以在 Hibernate HQL 中做 ->
Query q1 =session.createQuery(select i , j from A a JOIN B b on a.i = b.i)
List l1 = q1.list();
Query q2 = session.createQuery(select i , j from A a JOIN C b on a.i = b.i)
List l2 = q2.list();
然后你可以添加两个列表->
l1.addAll(l2);
视图是一种更好的方法,但由于 hql 通常返回 List 或 Set...您可以执行 list_1.addAll(list_2)。与工会相比完全糟糕,但应该工作。
也许我有一个更直接的问题要解决。我的“例如”在 JPA 中,Hibernate 作为 JPA 提供者。
我将三个选择(在第二种情况下是两个)拆分为多个选择,并组合了我自己返回的集合,有效地替换了“联合全部”。
我也经历过这种痛苦——如果查询是动态生成的(例如Hibernate Criteria),那么我找不到可行的方法来做到这一点。
对我来说好消息是我只是在调查联合以解决在 Oracle 数据库中使用“或”时的性能问题。
帕特里克发布的解决方案(使用集合以编程方式组合结果)虽然丑陋(特别是因为我也想做结果分页)对我来说已经足够了。
正如帕特里克所说,从每个SELECT中附加LIST是一个好主意,但请记住它的行为类似于UNION ALL。为避免这种副作用,只需控制对象是否已添加到最终集合中。如果没有,那么添加它。
您应该关心的另一件事是,如果您在每个SELECT中有任何JOIN,则结果将是对象数组()的列表,因此您必须对其进行迭代以仅保留您需要的对象。
希望它有效。List<Object[]>
这是一个特殊情况,但可能会激发您创建自己的工作。此处的目标是计算两个不同表中记录满足特定条件的记录总数。我相信这种技术适用于您需要跨多个表/源聚合数据的任何情况。
我有一些特殊的中间类设置,所以调用命名查询的代码很短而且很好用,但是你可以使用你通常使用的任何方法来执行你的查询。
QueryParms parms=new QueryParms();
parms.put("PROCDATE",PROCDATE);
Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT();
正如您在此处看到的,命名查询开始看起来非常像联合语句:
@Entity
@NamedQueries({
@NamedQuery(
name ="PIXEL_ALL",
query = "" +
" SELECT new SourceCount(" +
" (select count(a) from PIXEL_LOG_CURR1 a " +
" where to_char(a.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
" )," +
" (select count(b) from PIXEL_LOG_CURR2 b" +
" where to_char(b.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
" )" +
") from Dual1" +
""
)
})
public class SourceCount {
@Id
private Long COUNT;
public SourceCount(Long COUNT1, Long COUNT2) {
this.COUNT = COUNT1+COUNT2;
}
public Long getCOUNT() {
return COUNT;
}
public void setCOUNT(Long COUNT) {
this.COUNT = COUNT;
}
}
这里的部分魔法是创建一个虚拟表并将一条记录插入其中。在我的例子中,我将它命名为 dual1,因为我的数据库是 Oracle,但我认为你称之为虚拟表并不重要。
@Entity
@Table(name="DUAL1")
public class Dual1 {
@Id
Long ID;
}
不要忘记插入您的虚拟记录:
SQL> insert into dual1 values (1);