Java 冬眠效率';s表每子类继承策略
我正在考虑Hibernate托管类层次结构的表布局,当然,在一般意义上,每个子类的表技术对我来说是最合适的。然而,通过逻辑思考,我对它的性能有一些担心,特别是当子类的数量增加时 为了给出一个非常简短(经典)的示例,假设您有以下几个类:Java 冬眠效率';s表每子类继承策略,java,hibernate,database-design,inheritance,Java,Hibernate,Database Design,Inheritance,我正在考虑Hibernate托管类层次结构的表布局,当然,在一般意义上,每个子类的表技术对我来说是最合适的。然而,通过逻辑思考,我对它的性能有一些担心,特别是当子类的数量增加时 为了给出一个非常简短(经典)的示例,假设您有以下几个类: public abstract class Animal { int pkey; String name; } public class Dog extends Animal { long numSlippersChewed; // int i
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做了什么查询来拉出一个单独的动物?我至少可以想到两种情况:
Human
类的pet
字段。这将存储pkey,因此当Hibernate获取人类对象时,它还需要获取相应的动物对象。当给定动物的pkey时,Hibernate将使用什么查询(/ies)来提取和解压实际的动物数据,因为它可能驻留在Cat
或Dog
表中
,其中name='Rex'
(假设名称是唯一的)。这与上面的类似,因为它允许您标识超类表中的一行,但您不知道要检查哪个子类表以获取更多详细信息。HQL是否允许您从抽象类发出查询?(不过,使用特定于子类的东西可以很好地工作,例如来自Cat的,其中miceCaught>5
)exists
查询,然后从返回命中的表加载。或者,Hibernate可以在所有表中执行一些可怕的联合查询-基本上模拟每个层次结构方案的表,因为结果集将包括所有可能子类的属性,并且从子类表中选择的各个属性对于不相关的参数返回null
。后一种情况甚至可能需要添加一个合成鉴别器列,以便Hibernate能够知道哪个子类表实际返回了行,从而知道它们应该被解析成什么Java类
如果您有具体类型的子类型,情况也会变得更复杂:
public class Greyhound extends Dog {
float lifetimeRacingWinnings;
}
现在对于一个给定的动物pkey,在Dog
和灰狗
表中可能有有效的行,这意味着我手动检查与pkey对应的类的第一种方法变得更加困难
我如此担心的原因是,我希望在一个包含大约70个类的类层次结构上使用这种方法,最大嵌套链为4-5级,因此对所有这些类执行联合查询可能会有糟糕的性能。Hibernate是否有任何技巧来保持这种相对性能?或者pkey加载对其中一个类的引用需要很长时间吗?您会发现Hibernate使用一系列
LEFT JOIN
语句编写未知动物类型的查询,每个子类一个语句。因此,随着子类数量的增加,查询速度会变慢,并试图返回更广泛的结果集。因此,您是正确的,它不能很好地扩展大型类层次结构
使用HQL,可以直接查询子类并访问其属性。然后,将使用单个内部联接来呈现
我还没有尝试过多层次的继承。如果上面的内容还没有让您感到厌烦,建议您试试看——您可以打开SQL调试输出来查看发送到数据库的内容,或者简单地分析您的数据库。在我决定进行骨架测试之后
我创建了一个抽象超类,ADTestA
,以及三级层次结构中的25个具体子类(我希望您可以猜出它们的名称)。每个类都有一个名称与其字母对应的整数字段,因此,例如,类ADTestG
除了从直接父类ADTestB
继承的b
字段之外,还有一个int字段g
,以及顶级抽象超类中的pkey
和a
字段
从ADTestA发出HQL查询,其中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
的字段进行的一对一映射总是会涉及这种查找
(另一方面,替代策略也不是闪亮的希望灯塔;按照层次结构路由查找表,并在一个表中包含数百列,听起来也不是很有效……)只要您仅通过Hibernate访问数据库,并且您没有重要数据,或者准备编写一个小的迁移脚本,您就应该能够在开发过程的后期就每个子类/层次结构的表做出决定。这就是ORM的优点,它抽象了数据库结构
另一方面,我是“更喜欢组合而不是继承”(prefercomposition over inheritation)的忠实粉丝,我很怀疑一个拥有70个类的模型是否