多态数据库设计

关系数据库虽然说是对“对象”进行的建模,但是很令人惊讶的是它却不原生的支持 OOP 的最基本概念,继承与多态。常见的 ORM 框架都会通过一定的“策略”来辅助实现多态。常见的策略有下面三种:

  • table per class hierarchy:仅使用一个 table 表示单根上所有的类,很自然每行都有“所有的成员”,因此可能很浪费空间
  • table per subclass:为父类创建一个 table,每个子类也有一个 table,父类的 primary key 对应子类是 1-to-1 的,这样父类可以存放其中公用的部分而子类里面存放的是自己的部分。这种实现还常常在父类里面加上一个 discriminator 用来区分不同子类。很显然这个方法可以与前者混用
  • table per concrete class:每个实现类一个单独的 table,里面含有所有的成员,所以实际上你可以认为就是为每个类提供了单独的表,表之间没有任何关系。

hibernate 提供了这三种支持,另外还有所谓的 implicit polymorphism 的支持(可以看成是最后一种 case 的简化版本,可以做 generator 但是就不能自动的 union 了)。以上三种实现有自己的局限性和合适的地方

  • per class hierarchy 除了浪费了空间以外,一般必须使用一个 discriminator 来标识类,好处是一个 table 做 migration 容易,某些情况下还是可以避免自身的缺点的,比如子类只是名称不一样,常见的是存放某些类型的时候不同的类型可以公用一个 table,他们各自依靠单独的 discriminator 取值来决定在这个表里面自己的范围;稍微复杂一点的是子类的“类型”不一样但是可以都转换成为某个东西来存放(如都能转换成字符串)
  • per subclass 好处自然是做 DB migration 方便,多数的实现是依赖于 table 的 join,也可以一次获得所有子类的对象,但是性能不如前者高,discriminator 不是必需的,空间上算是比较合理的,适合于不太常需要获得所有对象,每类又有很多对象的情况
  • per concrete class 在 migration 的时候比较不利,访问单独每类性能比较好,适合获得根类所有的对象(使用 union 获得),特别是父类并没有什么东西的情况;另一个重要的局限是因为没有 shared primary key,因此不利于与其他的发生 join 关系(比如父类与别的对象有 one-to-many 的关系),这类父类的 id 不能自动生成

JPA 的 annotation 里面我们可以用 @Inheritance 来标注,对应的 InheritanceType 为 SINGLE_TABLE、JOINED 和 TABLE_PER_CLASS。最后 @MappedSupperClass,它表示这个是公用的部分,形式上是继承,但并不一定表示多态的概念。

例子

比如我们希望存放几类 Job,每类 Job 都有不同的 Handler 来处理,一个合理的 abstraction 可以是使用 per subclass 的选择,结果通过父类 Job 传递到 handler 的 dispatcher,这可以用前面的 dynamic dispatching 的技术处理。通常 Job 有一些公用的属性,如名称、时间等,而特定的 Job 有一些自身的属性。一种解决途径是通过继承,在子类里面加上这些属性;另一种特殊情况是,如果这些属性虽不一样,但是可以统一成为 key/value 形式,key 类型和 value 类型都是兼容的,我们也可以考虑去掉 polymorphism,而将属性使用另一个表来表示,这种更加 generic 的做法会需要更多的 application code 来做检查,不似前者可以利用数据库的 non-null 等功能进行“静态”的映射。后者的实现很显然可以用 per class hierarchy 来做,将不同类型的属性使用子类来实现即可。

——————
And when the LORD saw that Leah was hated, he opened her womb: but Rachel was barren.

Advertisements
多态数据库设计

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s