我正在考虑Hibernate管理的类层次结构的表布局,当然每个子类技术的表格在一般意义上都是最合适的。但是,通过逻辑思考我对其性能有一些担忧,尤其是子类数量的缩放。
为了给出一个非常简短(和经典)的例子,假设您有以下类:
public abstract class Animal {
int pkey;
String name;
}
public class Dog extends Animal {
long numSlippersChewed; // int is not large enough...
}
public class Cat extends Animal {
short miceCaught; // ... but here int is far bigger than required :-)
}
(我正在消除getter和setter以及Hibernate映射等,只是假设它们是基本的明显案例)。
这些实体的数据库表是有意义的,你得到了很好的非规范化等等。但是,Hibernate为了拔出一只动物做了什么查询呢?我至少可以想到两种可能发生这种情况的情况:
pet
类的Human
字段。这将存储pkey,因此当Hibernate获取Human对象时,它也需要获取相应的Animal
对象。当给出动物的密钥时,Hibernate将使用什么查询(/ ies)来提取和解组实际的动物数据,因为它可以驻留在Cat
或Dog
表中?from Animal where name='Rex'
(假设名称是唯一的)。这类似于上面的内容,它允许您在超类表中标识一行,但您不知道要检查哪个子类表以获取更多详细信息。 HQL甚至允许您发出查询from
抽象类吗? (使用特定于子类的东西可以很好地工作,例如from Cat where miceCaught > 5
)。我可以想到两种方法可以在SQL中完成,而且看起来都不漂亮。一种是在给定pkey的每个子类表上运行exists
查询,然后从返回命中的表中加载。或者,Hibernate可以在所有表中执行一些可怕的联合查询 - 实质上模拟每层次表的方案,因为结果集将包括所有可能子类的属性,其中从子类表中返回null
的各个选项不相关的论点。后一种情况甚至可能需要添加一个合成的鉴别器列,以便Hibernate可以知道哪个子类表实际返回了行,从而知道应该将它们解析为什么Java类。
如果您有具体类型的子类型,事情也会变得更加毛茸茸:
public class Greyhound extends Dog {
float lifetimeRacingWinnings;
}
现在对于给定的动物pkey,Dog
和 Greyhound
表中可能存在有效行,这意味着我的第一种方法是手动检查对应的类一只小狗变得更加强硬。
我非常担心的原因是我希望在类层次结构上使用这种方法,其中包含大约70个类,最大嵌套链为4-5个级别,因此对所有这些类执行联合查询很可能有可怕的表现。 Hibernate是否有任何技巧可以保持相对高效?或者是通过pkey加载对其中一个类的引用需要很长时间?
答案 0 :(得分:8)
您会发现Hibernate使用一系列LEFT JOIN
语句为未知动物类型编写查询,每个子类一个。因此,随着子类数量的增加,查询将变慢,并将尝试返回更宽的结果集。所以你是对的,它不能很好地适应大型类层次结构。
使用HQL,是的,您可以直接查询子类,并访问其属性。然后将使用单个INNER JOIN
呈现。
我没有尝试过多级继承。如果上面还没有让你失望,建议你试试看 - 你可以打开SQL调试输出来查看发送到数据库的内容,或者只是简档数据库。
答案 1 :(得分:3)
在David M's helpful answer之后我决定将骨架测试放在一起。
我在三级层次结构中创建了一个抽象超类ADTestA
和25个具体的子类(我希望你可以猜出它们的名字)。每个类都有一个整数字段,其名称对应于其字母 - 例如,类ADTestG
除了从其立即继承的g
字段外,还有一个int字段b
父级ADTestB
以及顶级抽象超类中的pkey
和a
字段。
发出HQL查询from ADTestA where pkey=1
会产生以下SQL:
select adtesta0_.pkey as pkey0_, adtesta0_.a as a0_, adtesta0_1_.b as b1_,
adtesta0_2_.c as c2_, adtesta0_3_.d as d3_, adtesta0_4_.e as e4_,
adtesta0_5_.f as f5_, adtesta0_6_.g as g6_, adtesta0_7_.h as h7_,
adtesta0_8_.i as i8_, adtesta0_9_.j as j9_, adtesta0_10_.k as k10_,
adtesta0_11_.l as l11_, adtesta0_12_.m as m12_, adtesta0_13_.n as n13_,
adtesta0_14_.o as o14_, adtesta0_15_.p as p15_, adtesta0_16_.q as q16_,
adtesta0_17_.r as r17_, adtesta0_18_.s as s18_, adtesta0_19_.t as t19_,
adtesta0_20_.u as u20_, adtesta0_21_.v as v21_, adtesta0_22_.w as w22_,
adtesta0_23_.x as x23_, adtesta0_24_.y as y24_, adtesta0_25_.z as z25_,
case
when adtesta0_6_.pkey is not null then 6
when adtesta0_7_.pkey is not null then 7
when adtesta0_8_.pkey is not null then 8
when adtesta0_9_.pkey is not null then 9
when adtesta0_10_.pkey is not null then 10
when adtesta0_11_.pkey is not null then 11
when adtesta0_12_.pkey is not null then 12
when adtesta0_13_.pkey is not null then 13
when adtesta0_14_.pkey is not null then 14
when adtesta0_15_.pkey is not null then 15
when adtesta0_16_.pkey is not null then 16
when adtesta0_17_.pkey is not null then 17
when adtesta0_18_.pkey is not null then 18
when adtesta0_19_.pkey is not null then 19
when adtesta0_20_.pkey is not null then 20
when adtesta0_21_.pkey is not null then 21
when adtesta0_22_.pkey is not null then 22
when adtesta0_23_.pkey is not null then 23
when adtesta0_24_.pkey is not null then 24
when adtesta0_25_.pkey is not null then 25
when adtesta0_1_.pkey is not null then 1
when adtesta0_2_.pkey is not null then 2
when adtesta0_3_.pkey is not null then 3
when adtesta0_4_.pkey is not null then 4
when adtesta0_5_.pkey is not null then 5
when adtesta0_.pkey is not null then 0
end as clazz_
from ADTestA adtesta0_
left outer join ADTestB adtesta0_1_ on adtesta0_.pkey=adtesta0_1_.pkey
left outer join ADTestC adtesta0_2_ on adtesta0_.pkey=adtesta0_2_.pkey
left outer join ADTestD adtesta0_3_ on adtesta0_.pkey=adtesta0_3_.pkey
left outer join ADTestE adtesta0_4_ on adtesta0_.pkey=adtesta0_4_.pkey
left outer join ADTestF adtesta0_5_ on adtesta0_.pkey=adtesta0_5_.pkey
left outer join ADTestG adtesta0_6_ on adtesta0_.pkey=adtesta0_6_.pkey
left outer join ADTestH adtesta0_7_ on adtesta0_.pkey=adtesta0_7_.pkey
left outer join ADTestI adtesta0_8_ on adtesta0_.pkey=adtesta0_8_.pkey
left outer join ADTestJ adtesta0_9_ on adtesta0_.pkey=adtesta0_9_.pkey
left outer join ADTestK adtesta0_10_ on adtesta0_.pkey=adtesta0_10_.pkey
left outer join ADTestL adtesta0_11_ on adtesta0_.pkey=adtesta0_11_.pkey
left outer join ADTestM adtesta0_12_ on adtesta0_.pkey=adtesta0_12_.pkey
left outer join ADTestN adtesta0_13_ on adtesta0_.pkey=adtesta0_13_.pkey
left outer join ADTestO adtesta0_14_ on adtesta0_.pkey=adtesta0_14_.pkey
left outer join ADTestP adtesta0_15_ on adtesta0_.pkey=adtesta0_15_.pkey
left outer join ADTestQ adtesta0_16_ on adtesta0_.pkey=adtesta0_16_.pkey
left outer join ADTestR adtesta0_17_ on adtesta0_.pkey=adtesta0_17_.pkey
left outer join ADTestS adtesta0_18_ on adtesta0_.pkey=adtesta0_18_.pkey
left outer join ADTestT adtesta0_19_ on adtesta0_.pkey=adtesta0_19_.pkey
left outer join ADTestU adtesta0_20_ on adtesta0_.pkey=adtesta0_20_.pkey
left outer join ADTestV adtesta0_21_ on adtesta0_.pkey=adtesta0_21_.pkey
left outer join ADTestW adtesta0_22_ on adtesta0_.pkey=adtesta0_22_.pkey
left outer join ADTestX adtesta0_23_ on adtesta0_.pkey=adtesta0_23_.pkey
left outer join ADTestY adtesta0_24_ on adtesta0_.pkey=adtesta0_24_.pkey
left outer join ADTestZ adtesta0_25_ on adtesta0_.pkey=adtesta0_25_.pkey
where adtesta0_.pkey=1
这不是很漂亮,并且确实对应于我希望可以避免的每层次表的有效模拟。
所以看起来这些查询会非常昂贵。我会考虑一下他们需要多长时间(比如说,知道我想要一个ADTestP
的实例,并要求其中一个实际上只是加入所需的父表) 。然而,我有一种感觉,这对于其他实体的参考是不可避免的;换句话说,来自ADTestA
类型字段的一对一映射总是涉及这种查找。
(另一方面,替代策略也没有发出希望的信号;按照每层次的表格路线,在单个表格中有数百个列,听起来效率也不高......)< / p>
答案 2 :(得分:1)
只要您通过Hibernate访问数据库并且您没有重要数据或准备编写小型迁移脚本,您就应该能够在开发过程中很晚就每个子类/层次结构做出表决定。这就是ORM的美妙之处,它抽象了数据库结构...
另一方面,我是“喜欢构图而不是继承”(Prefer composition over inheritance?)的忠实粉丝,我很怀疑是否有一个超过4-5级的70个班级的模型无法简化......但是我会让你自己思考一下,毕竟我不知道你想要什么样的思考。