Эффективность стратегии наследования таблицы на подкласс 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 :-)
}

(Я игнорирую методов get и методы set и В спящем режиме отображения и т.д., просто предполагаю, что они - основной очевидный случай).

Таблицы базы данных для этих объектов имеют смысл, Вы получаете хорошую денормализацию и так далее. Однако, какой запрос действительно В спящем режиме, делают для вытаскивания отдельного животного? Я могу думать о по крайней мере двух случаях, где это могло бы произойти:

  1. Некоторый другой объект, имеющий непосредственное (или one-many) отображение, такое как a pet поле a Human класс. Это сохранило бы pkey, поэтому когда В спящем режиме, выбирает Человеческий объект, он должен будет выбрать соответствие Animal объект также. При предоставлении pkey животного, что запрос (/ies) Будет в спящем режиме использование для извлечения и неМаршалл фактические данные Животных, учитывая, что это могло находиться в Cat или Dog таблицы?
  2. HQL такой как from Animal where name='Rex' (давайте возьмем имена, уникальны). Это подобно вышеупомянутому, в котором это позволяет Вам определить строку в таблице суперкласса, но Вы не знаете который таблица подкласса осмотреть для получения дальнейшей информации. Делает HQL, даже позволяют Вам выпустить запрос from абстрактный класс? (Используя подкласс определенный материал работает приятно хотя, например. from Cat where miceCaught > 5).

Я могу думать о двух способах, которыми это могло быть сделано в SQL, и ни один не кажется симпатичным. Нужно работать exists запрос на каждой таблице подкласса для данного pkey и затем загружается от таблицы, которая возвратила хит. Кроме того, Будьте в спящем режиме, мог выполнить некоторый ужасный запрос на объединение, участвующий во всех таблицах - по существу моделирование схемы таблицы на иерархию, в которую набор результатов будет включать атрибуты для всех возможных подклассов с человеком, выбирает из возврата таблиц подкласса null для несоответствующих аргументов. Этот последний случай должен был бы, вероятно, даже добавить синтетический столбец различителя так, чтобы Были в спящем режиме, мог знать, какая таблица подкласса на самом деле возвратила строку и таким образом в какой класс Java они должны быть проанализированы.


Вещи становятся более волосатыми также, если у Вас есть подтипы конкретных типов:

public class Greyhound extends Dog {
   float lifetimeRacingWinnings;
}

Теперь для данного животного pkey, могут быть допустимые строки в Dog и Greyhound таблицы, означая, что мой первый подход ручной проверки класса, который соответствует pkey, становится намного более жестким.

Причина, которую я так заинтересован, состоит в том, что я буду желать использовать этот подход к иерархии классов приблизительно с 70 классами с максимальной вложенной цепочкой 4-5 уровней, так выполнение запроса на объединение на всем этом, вероятно, будет иметь ужасную производительность. Действительно В спящем режиме имеют какие-либо приемы его рукав для хранения этого относительно производительным? Или действительно ли загрузка является ссылкой на один из этих классов pkey, собирающимся занять много времени?

13
задан Andrzej Doyle 7 January 2010 в 12:37
поделиться

3 ответа

Вы обнаружите, что Хибернат записывает запрос для неизвестного типа животных с серией левых операторов JOIN, по одному на подкласс. Таким образом, запрос будет замедляться по мере увеличения числа подклассов и будет пытаться вернуть еще более широкий набор результатов. Таким образом, Вы правы, это не очень хорошо масштабируется с большими иерархиями классов.

С HQL, да, Вы можете запросить подкласс напрямую, и получить доступ к его свойствам. Это будет отрисовываться с помощью единственного INNER JOIN.

Я не пробовал это делать с несколькими уровнями наследования. Если вышесказанное еще не отпугнуло вас, предлагаю попробовать и посмотреть - вы можете включить отладочный вывод SQL, чтобы посмотреть, что отправляется в базу данных, или просто профилировать вашу базу данных.

.
8
ответ дан 2 December 2019 в 00:58
поделиться

После полезного ответа Дэвида М я решил собрать вместе скелетный тест.

Я создал абстрактный суперкласс, ADTestA, и 25 конкретных подклассов в трехуровневой иерархии (я полагаю, вы можете угадать их имена). Каждый класс имел одно целое поле с именем, соответствующим его букве - так, например, класс ADTestG имеет одно целое поле int g в дополнение к полю b, которое он наследует от своего непосредственного родителя ADTestB, а также поля pkey и a от абстрактного суперкласса верхнего уровня.

Выдача запроса на HQL из ADTestA, где 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 всегда будет включать именно такой вид поиска.

(С другой стороны, альтернативные стратегии также не являются блестящими маяками надежды; идти по маршруту табличной иерархии и иметь буквально сотни столбцов в одной таблице тоже звучит не очень эффективно...)

.
3
ответ дан 2 December 2019 в 00:58
поделиться

До тех пор, пока вы получаете доступ к своей БД только через Hibernate и либо не имеете важных данных, либо готовы написать небольшой скрипт миграции, вы должны быть в состоянии принять решение о таблице по подклассу / иерархии довольно поздно в процессе разработки. В этом вся прелесть ORM, он абстрагирует структуру БД...

С другой стороны, я большой поклонник "предпочитаю композицию, а не наследование" (Предпочитаете композицию, а не наследование?) и я довольно сомневаюсь, что модель с 70 классами на 4-5 уровнях не может быть упрощена... но я позволю вам подумать об этом, в конце концов, я не знаю, какой зонд вы пытаетесь совать.

.
1
ответ дан 2 December 2019 в 00:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: