Hibernate ORM и "ленивый" JOIN
От: Blazkowicz Россия  
Дата: 08.05.07 13:10
Оценка:
Всем привет!

К сожалени в послденее время в связи со спецификой работы я все больше теоретик чем практик. Поэтому вроде бы простые задачи порой ставят в тупик. Вот столкнулся с практической ситуацией и решил её кривым костылем. Был бы благодарен если общественность порсвятит меня на счет возможных решений.

Суть проблемы. Есть две сущности в БД. И ассоциация между ними. Через FK.

HBPhoneCallBean и HBSubjectBean. В 99% случаев в проекте эта ассоциация не нужна. Но вот появился случай когда она понадобилась. И вот мои догадки по которым бы хотелось получить комментарии.
1) Если банально прописать маппинг many-to-one, то получается HBSubjectBean будет выбиратся в 99% случаев где он не нужен. Создавая дополнительную нагрузку на базу. Вопрос, если прописать fetch="join", получается выборка будет через джоин. Значит ли что оверхед при этом будет довольно мал. И им можно было бы принебречь?
2) Если many-to-one сделать ленивым, то в 99% оверхеда не будет. Зато проблемы начнутся в моем единственном случае. Так как выборка происходит сразу толстого списка HBPhoneCallBean, то при ленивой загрузке HBSubjectBean будет выбрана каким-нибудь тормозным способом, подозреваю что отделmным запосом на каждый HBPhoneCallBean. Что убъет производительносьт операции наповал.

После этих раздумий я сделал через Ж. Благо весь проект такой и моей маленькой лажи никто не заметит.

public class HBPhoneCallBean extends HBTrackingBean{

    public static class JoinInmate extends HBPhoneCallBean{
        private HBSubjectBean subject;
        public HBSubjectBean getSubject(){
            return subject;
        }
        public void setSubject(HBSubjectBean subject){
            this.subject = subject;
        }
    }



    <class
        name="HBPhoneCallBean$JoinInmate"
        polymorphism="explicit"
        table="subject_telephone_call">
        <id name="pk" column="pk" type="java.lang.Long">
            <generator class="native"/>
        </id>

        <many-to-one
            name="subject"
            column="fk_subject"
            class="HBSubjectBean"
            insert="false"
            update="false"
            />


Аттрибут polymorphism="explicit" оказался критическим дабы предотвратить дублирование данных разных классов при выборке списка.

В глаза бросаются сразу 2 недостатка. Дублирование маппинга и введение новой сущности только для того чтобы решить порблему уровня persistence.

Буду премногоблагодарен за комментарии и рекомендации как решить проблему одними только маппингами.
Re: Hibernate ORM и "ленивый" JOIN
От: C0s Россия  
Дата: 08.05.07 13:24
Оценка: 7 (1)
Здравствуйте, Blazkowicz, Вы писали:

B>1) Если банально прописать маппинг many-to-one, то получается HBSubjectBean будет выбиратся в 99% случаев где он не нужен. Создавая дополнительную нагрузку на базу. Вопрос, если прописать fetch="join", получается выборка будет через джоин. Значит ли что оверхед при этом будет довольно мал. И им можно было бы принебречь?


лучше join не делать, хз, как поведёт себя БД, может справится, а может и нет

B>После этих раздумий я сделал через Ж. Благо весь проект такой и моей маленькой лажи никто не заметит.


я бы так не делал =)

B>Буду премногоблагодарен за комментарии и рекомендации как решить проблему одними только маппингами.


почему только маппингами?
я бы как раз посоветовал тюнить код выборки, т.е. для общего случая оставить lazy, а в конкретном уж разобраться так, чтобы отскакивало

B>2) Если many-to-one сделать ленивым, то в 99% оверхеда не будет. Зато проблемы начнутся в моем единственном случае. Так как выборка происходит сразу толстого списка HBPhoneCallBean, то при ленивой загрузке HBSubjectBean будет выбрана каким-нибудь тормозным способом, подозреваю что отделmным запосом на каждый HBPhoneCallBean. Что убъет производительносьт операции наповал.


как ты выбираешь "толстый список"?
например, если через Criteria, то укажи ему явно FetchMode для нужной связки
Re[2]: Hibernate ORM и "ленивый" JOIN
От: Blazkowicz Россия  
Дата: 08.05.07 13:37
Оценка:
Здравствуйте, C0s, Вы писали:

C0s>лучше join не делать, хз, как поведёт себя БД, может справится, а может и нет

Тоесть я верно опасался что джоин таки способен нагнуть базу?

B>>После этих раздумий я сделал через Ж. Благо весь проект такой и моей маленькой лажи никто не заметит.

C0s>я бы так не делал =)
Да. Меня совесть грызет. Вот и любопытствую.

B>>Буду премногоблагодарен за комментарии и рекомендации как решить проблему одними только маппингами.


C0s>почему только маппингами?

C0s>я бы как раз посоветовал тюнить код выборки, т.е. для общего случая оставить lazy, а в конкретном уж разобраться так, чтобы отскакивало
Не очень понял как тюнить чтобы отскакивало? Смысл то в том что с lazy все равно как минимум 2 запроса будет. А хотелось бы одним.

B>>2) Если many-to-one сделать ленивым, то в 99% оверхеда не будет. Зато проблемы начнутся в моем единственном случае. Так как выборка происходит сразу толстого списка HBPhoneCallBean, то при ленивой загрузке HBSubjectBean будет выбрана каким-нибудь тормозным способом, подозреваю что отделmным запосом на каждый HBPhoneCallBean. Что убъет производительносьт операции наповал.


C0s>как ты выбираешь "толстый список"?

C0s>например, если через Criteria, то укажи ему явно FetchMode для нужной связки
Да. criteria.list()
То есть ты хочешь сказать если сделать Criteria.setFetchMode(JOIN) то не смотря на lazy в маппинге ассоциация приедет по этому же запросу с минимальными обращениями к БД?
Re[3]: Hibernate ORM и "ленивый" JOIN
От: C0s Россия  
Дата: 08.05.07 13:42
Оценка: 21 (1)
Здравствуйте, Blazkowicz, Вы писали:

C0s>>лучше join не делать, хз, как поведёт себя БД, может справится, а может и нет

B>Тоесть я верно опасался что джоин таки способен нагнуть базу?

дял верности можешь в форуме по DB спросить ... я так полагаю, что всё упрётся в статистику, но в любом случае не стоит заставлять БД делать то, что делать не надо

B>Не очень понял как тюнить чтобы отскакивало? Смысл то в том что с lazy все равно как минимум 2 запроса будет. А хотелось бы одним.


так это была прелюдия к тому, что я написал ниже про setFetchMode

B>Да. criteria.list()

B>То есть ты хочешь сказать если сделать Criteria.setFetchMode(JOIN) то не смотря на lazy в маппинге ассоциация приедет по этому же запросу с минимальными обращениями к БД?

только там два параметра:
    public Criteria setFetchMode(String associationPath, FetchMode mode) throws HibernateException;

и вызов этого метода должен помочь тебе. проверь, напиши потом сюда =)
Re[4]: Hibernate ORM и "ленивый" JOIN
От: Blazkowicz Россия  
Дата: 08.05.07 13:56
Оценка:
Здравствуйте, C0s, Вы писали:

B>>Да. criteria.list()

B>>То есть ты хочешь сказать если сделать Criteria.setFetchMode(JOIN) то не смотря на lazy в маппинге ассоциация приедет по этому же запросу с минимальными обращениями к БД?

C0s>только там два параметра:

C0s>
C0s>    public Criteria setFetchMode(String associationPath, FetchMode mode) throws HibernateException;
C0s>

Да. это я так... образно.

C0s>и вызов этого метода должен помочь тебе. проверь, напиши потом сюда =)

Судя по докам все верно ты говоришь. Должно помочь, не знаю появится ли шанс испытать. Но если появится — отпишу.
Re[5]: Hibernate ORM и "ленивый" JOIN
От: C0s Россия  
Дата: 08.05.07 14:03
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

C0s>>и вызов этого метода должен помочь тебе. проверь, напиши потом сюда =)

B>Судя по докам все верно ты говоришь. Должно помочь, не знаю появится ли шанс испытать. Но если появится — отпишу.

года 2-3 назад я этот метод успешно использовал для обратной операции, т.е. у ряда объектов были прилинкоываны доп. атрибуты, которые по умолчанию (в мэпинге так было задано) вынимались через join, потому что были нужны в 90%, если даже не чаще. а для постраничных запросов их приходилось через этом метод "отсоединять", иначе они портили картину, известно ведь, что firstresult/maxresults ориентированы на DB, а не объекты.

и вызов
criteria.setFetchMode("obj.attrs", FetchMode.LAZY);
работал там нормально =) — еще на hibernate 2.x. и если багов не появилось, то будет работать и в твоём случае
Re[6]: Hibernate ORM и "ленивый" JOIN
От: Blazkowicz Россия  
Дата: 08.05.07 14:10
Оценка:
Здравствуйте, C0s, Вы писали:

C0s>и вызов
criteria.setFetchMode("obj.attrs", FetchMode.LAZY);
работал там нормально =) — еще на hibernate 2.x. и если багов не появилось, то будет работать и в твоём случае

У меня как раз 2.x. Камрады умудрились написать его так что портировать на 3.х в разумное время не представляется возможным.
Re[5]: Hibernate ORM и "ленивый" JOIN
От: danila.master Россия  
Дата: 08.05.07 14:48
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, C0s, Вы писали:


B>>>Да. criteria.list()

B>>>То есть ты хочешь сказать если сделать Criteria.setFetchMode(JOIN) то не смотря на lazy в маппинге ассоциация приедет по этому же запросу с минимальными обращениями к БД?

C0s>>только там два параметра:

C0s>>
C0s>>    public Criteria setFetchMode(String associationPath, FetchMode mode) throws HibernateException;
C0s>>

B>Да. это я так... образно.

C0s>>и вызов этого метода должен помочь тебе. проверь, напиши потом сюда =)

B>Судя по докам все верно ты говоришь. Должно помочь, не знаю появится ли шанс испытать. Но если появится — отпишу.

Если с fetchMode не получится, есть еще вариант использовать batch fetching. Тогда (при lazy-загрузки) генерятся запросы вида:
select ... from table where id in (?, ?, ?, ?, ...)

Это конечно хуже, чем join, но для постраничного вывода может подойти.
Re[6]: Hibernate ORM и "ленивый" JOIN
От: Blazkowicz Россия  
Дата: 08.05.07 14:56
Оценка:
Здравствуйте, danila.master, Вы писали:

DM>Если с fetchMode не получится, есть еще вариант использовать batch fetching. Тогда (при lazy-загрузки) генерятся запросы вида:

DM>
DM>select ... from table where id in (?, ?, ?, ?, ...)
DM>

DM>Это конечно хуже, чем join, но для постраничного вывода может подойти.

Не, спасибо, вот этого точно не надо. Я уже видел на другом проекте исключение на Оракле, когда в in было больше тысячи элементов. Да и по производительности такой запрос не из лучших.
Re[7]: Hibernate ORM и "ленивый" JOIN
От: C0s Россия  
Дата: 08.05.07 14:57
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

DM>>
DM>>select ... from table where id in (?, ?, ?, ?, ...)
DM>>

DM>>Это конечно хуже, чем join, но для постраничного вывода может подойти.

B>Не, спасибо, вот этого точно не надо. Я уже видел на другом проекте исключение на Оракле, когда в in было больше тысячи элементов. Да и по производительности такой запрос не из лучших.


однако, fetchSize больше 1000 — это жестковато
Re[8]: Hibernate ORM и "ленивый" JOIN
От: Blazkowicz Россия  
Дата: 08.05.07 14:59
Оценка:
Здравствуйте, C0s, Вы писали:

B>>Не, спасибо, вот этого точно не надо. Я уже видел на другом проекте исключение на Оракле, когда в in было больше тысячи элементов. Да и по производительности такой запрос не из лучших.


C0s>однако, fetchSize больше 1000 — это жестковато


Ну, там был не Hibernate а SQL генерился. Вот и нагенерилось. Конечно же потом переделали на JOIN.
Re[7]: Hibernate ORM и "ленивый" JOIN
От: danila.master Россия  
Дата: 08.05.07 15:06
Оценка:
Здравствуйте, Blazkowicz, Вы писали:

B>Здравствуйте, danila.master, Вы писали:


DM>>Если с fetchMode не получится, есть еще вариант использовать batch fetching. Тогда (при lazy-загрузки) генерятся запросы вида:

DM>>
DM>>select ... from table where id in (?, ?, ?, ?, ...)
DM>>

DM>>Это конечно хуже, чем join, но для постраничного вывода может подойти.

B>Не, спасибо, вот этого точно не надо. Я уже видел на другом проекте исключение на Оракле, когда в in было больше тысячи элементов. Да и по производительности такой запрос не из лучших.


Хуже, кончено, чем join. Но значительно лучше последовательных запросов where id = ?.

Вобще там не обязательно все элементы сразу выбирать. Можно, например, 10 запросов по 10 штук настроить. Если запрашивается большой объем данных, а светится только небольшая часть, этот подход может быть эффективен.

А тысяча элементов в in — это хорошо
Re[2]: Hibernate ORM и "ленивый" JOIN
От: Cyberax Марс  
Дата: 08.05.07 18:18
Оценка: 7 (2)
Здравствуйте, C0s, Вы писали:

C0s>как ты выбираешь "толстый список"?

C0s>например, если через Criteria, то укажи ему явно FetchMode для нужной связки
В обычном HQL тоже можно. Из моего кода:
select distinct(user) from SystemUser user inner join fetch user.roles child where child.faId= ...
Sapienti sat!
Re[3]: Hibernate ORM и "ленивый" JOIN
От: C0s Россия  
Дата: 08.05.07 18:20
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>В обычном HQL тоже можно. Из моего кода:

C>select distinct(user) from SystemUser user inner join fetch user.roles child where child.faId= ...


ага, верно. просто у меня под рукой не было примера, а я точно не помнил, и писать не стал
Re: Hibernate ORM и "ленивый" JOIN
От: lexius www.acula.org
Дата: 08.05.07 19:04
Оценка:
Забавно, прочтенное здесь заняло меня на пару часиков оптимизации кода
************
www.acula.org
Re: Hibernate ORM и "ленивый" JOIN
От: dshe  
Дата: 10.05.07 07:18
Оценка: 6 (1)
Здравствуйте, Blazkowicz, Вы писали:

B>1) Если банально прописать маппинг many-to-one, то получается HBSubjectBean будет выбиратся в 99% случаев где он не нужен. Создавая дополнительную нагрузку на базу. Вопрос, если прописать fetch="join", получается выборка будет через джоин. Значит ли что оверхед при этом будет довольно мал. И им можно было бы принебречь?


На мой взгляд, существуют несколько случаев, где join может дать хуже эффект, чем банальные одиночные селекты.

Допустим у нас есть сущность A и связанная с ней сущность B так, что одной записи A соответсвует много B.
A
|
^
B

Запрос с join будет выглядеть приблизительно так
select A.*, B.*
from A
left outer join B on A.id = B.a_id
where A.id = :id
;

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

Ситуация может усугубляться, когда join'ом вытягиваются несколько уровней иерархии.
A
|
^
B
|
^
C

select A.*, B.*, C.*
from A
left outer join B on A.id = B.a_id
left outer join C on B.id = C.b_id
where A.id = :id
;

Т.е. если с одной записью A связано 100 записей B, а с каждой B связано еще по 100 С, то получится, что информация об одном A будет дублироваться 10000 раз!

Еще один неприятный эффект появляется когда мы join'ом вытягиваем записи параллельных иерархий.
  A
 / \
^   ^
B   D

select A.*, B.*, D.*
from A
left outer join B on A.id = B.a_id
left outer join D on A.id = D.a_id
where A.id = :id
;
--
Дмитро
Re[2]: Hibernate ORM и "ленивый" JOIN
От: tavr  
Дата: 10.05.07 10:48
Оценка: 1 (1)
Здравствуйте, dshe, Вы писали:

D>На мой взгляд, существуют несколько случаев, где join может дать хуже эффект, чем банальные одиночные селекты.


D>Допустим у нас есть сущность A и связанная с ней сущность B так, что одной записи A соответсвует много B.

D>При это следует отметить, что информация о записи A будет дублироваться для каждой записи B. Т.е. информации будет передаваться больше, чем это реально необходимо. Этот эффект будет особенно ощутим, когда размер записи A (в байтах) велик и велико количество записей B, связанных с одним A.
давайте посчитаем: размер одной записи A возьмем 1 Кбайт, количество ассоциированных записей B — 1025
ясно, что придется переслать лишний 1 Мбайт, при скорости нашей сети 100 Мбит получаем задержку в 0,08 секунды
это все в предположении, что лишние данные действительно едут по сети, в чем есть большие сомнения, в любом случае все зависит от драйверов конкретной базы данных
Re[3]: Hibernate ORM и "ленивый" JOIN
От: dshe  
Дата: 10.05.07 14:16
Оценка:
Здравствуйте, tavr, Вы писали:

T>Здравствуйте, dshe, Вы писали:


D>>На мой взгляд, существуют несколько случаев, где join может дать хуже эффект, чем банальные одиночные селекты.


D>>Допустим у нас есть сущность A и связанная с ней сущность B так, что одной записи A соответсвует много B.

D>>При это следует отметить, что информация о записи A будет дублироваться для каждой записи B. Т.е. информации будет передаваться больше, чем это реально необходимо. Этот эффект будет особенно ощутим, когда размер записи A (в байтах) велик и велико количество записей B, связанных с одним A.
T>давайте посчитаем: размер одной записи A возьмем 1 Кбайт, количество ассоциированных записей B — 1025
T>ясно, что придется переслать лишний 1 Мбайт, при скорости нашей сети 100 Мбит получаем задержку в 0,08 секунды
T>это все в предположении, что лишние данные действительно едут по сети, в чем есть большие сомнения, в любом случае все зависит от драйверов конкретной базы данных

Често говоря, я не думаю, что существуют такие интеллектуальные драйвера, которые не передают лишние данные. Впрочем, если есть, буду рад получить на них линки.

join сам по себе не плох, но с его помощью можно легко получить объемные resultset'ы. В приведенном выше примере достаточно подвязать к A 10 записей D и получить 10M и более ощутимое время 0.8 s (согласно приведенным выше вычислениям).
--
Дмитро
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.