Здравствуйте, bart simpson, Вы писали:
BS>Подскажите в каком направление смотреть, что бы реализовать lazy загрузку коллекций.
Загрузка коллекций в Hibernate 3 и так lazy по-умолчанию. Никаких специальных усилий для этого прилагать не нужно.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, bart simpson, Вы писали:
BS>>Подскажите в каком направление смотреть, что бы реализовать lazy загрузку коллекций. B>Загрузка коллекций в Hibernate 3 и так lazy по-умолчанию. Никаких специальных усилий для этого прилагать не нужно.
Это. да сори не описал суть проблемы, при получении коллекции выходит ошибка org.hibernate.LazyInitializationException: could not initialize proxy — no Session, как правильнее сделать удержание транзакции. Воспользоваться HibernateInterceptor или что нить другое? Или где можно про это посмотреть?
BS>Это. да сори не описал суть проблемы, при получении коллекции выходит ошибка org.hibernate.LazyInitializationException: could not initialize proxy — no Session, как правильнее сделать удержание транзакции. Воспользоваться HibernateInterceptor или что нить другое? Или где можно про это посмотреть?
Есть, конечно, open session in view. Но это не самый лучший подход. Правильнее задать себе вопрос, если мне нужна эта коллекция, то почему она ещё не загружена? Почему в DAO или сервисе не указан требуемый FetchMode для неё?
Здравствуйте, Blazkowicz, Вы писали:
B>Правильнее задать себе вопрос, если мне нужна эта коллекция, то почему она ещё не загружена? Почему в DAO или сервисе не указан требуемый FetchMode для неё?
Потому что на момент загрузки сущностей непонятно, нужна эта коллекция или нет. Передавать из слоя маппинга DAO объектов кучу параметров, которые точно укажут, что нужно, а что нет, бывает очень утомительно.
Здравствуйте, Donz, Вы писали:
D>Потому что на момент загрузки сущностей непонятно, нужна эта коллекция или нет.
Это аналогично фразе "в данный конкретный момент времени не понятно что должна делать программа".
D>Передавать из слоя маппинга DAO объектов кучу параметров, которые точно укажут, что нужно, а что нет, бывает очень утомительно.
Что за "слой маппинг DAO"?
В MVC контроллере и View мы всегда знаем какой у нас происходит сценарий\use case.
Слой сервиса, он же Transaction Script является реализацией нужного сценария. В каждом сценарии мы точно знаем какие зависимости нам нужны. Поэтому логичней всего является передача параметров из сервиса в DAO. Хотя, зачастую, так как DAO скрывает реализацию работы с API, то просто делаются отдельные DAO методы под разные ситуации.
Никаких утомительных сложностей в этом подходе пока не обнаружил. Как раз наоборот, такой подход позволяет избежать возникновения N+1, потому что девелопер получает исключение и сразу продумывает необходимую стратегию для выборки сущности. Это лучше чем через пол года разработки получить несколько десятков сценариев, которые потом отдельно придется оптимизировать, чтобы избежать N+1.
On 04.09.2012 12:11, Blazkowicz wrote:
> В MVC контроллере и View мы всегда знаем какой у нас происходит > сценарий\use case. > Слой сервиса, он же Transaction Script является реализацией нужного > сценария. В каждом сценарии мы точно знаем какие зависимости нам нужны. > Поэтому логичней всего является передача параметров из сервиса в DAO. > Хотя, зачастую, так как DAO скрывает реализацию работы с API, то просто > делаются отдельные DAO методы под разные ситуации.
Зачем это всё при наличии возможности сделать transaction per request —
какие вы видите проблемы в этом подходе?
> Никаких утомительных сложностей в этом подходе пока не обнаружил. Как > раз наоборот, такой подход позволяет избежать возникновения N+1, потому > что девелопер получает исключение и сразу продумывает необходимую > стратегию для выборки сущности.
А какой у него есть выбор кроме EAGER и LAZY? К тому же ЕМНИП сделать
EAGER более чем одной ассоциированной коллекции (OneToMany, ManyToMany)
нельзя.
> Это лучше чем через пол года разработки > получить несколько десятков сценариев, которые потом отдельно придется > оптимизировать, чтобы избежать N+1.
Там, где имеет смысл реально оптимизировать под каждый сценарий
(приличная нагрузка + много данных + нельзя использовать 2nd level
cache) — проще выкинуть ORM вообще и чем раньше тем лучше.
Здравствуйте, Blazkowicz, Вы писали:
D>>Потому что на момент загрузки сущностей непонятно, нужна эта коллекция или нет. B>Это аналогично фразе "в данный конкретный момент времени не понятно что должна делать программа".
Нет, это означает абстрагированность сервисного и DAO слоя от слоя представления данных.
D>>Передавать из слоя маппинга DAO объектов кучу параметров, которые точно укажут, что нужно, а что нет, бывает очень утомительно. B>Что за "слой маппинг DAO"?
Маппинг DAO объектов на DTO объекты или на вью.
B>В MVC контроллере и View мы всегда знаем какой у нас происходит сценарий\use case.
Но не факт, что мы горим желанием на каждый новый сценарий, минорно отличающийся от уже существующих, плодить код в сервисном слое.
B>Слой сервиса, он же Transaction Script является реализацией нужного сценария. В каждом сценарии мы точно знаем какие зависимости нам нужны. Поэтому логичней всего является передача параметров из сервиса в DAO. Хотя, зачастую, так как DAO скрывает реализацию работы с API, то просто делаются отдельные DAO методы под разные ситуации.
Вот и не хочется плодить DAO методы, а под них сервисные методы. Изначально хотим достать всех учеников класса. Потом нужно достать учеников со всеми родителями, потом с учителями, потом с учителями и родителями, потом с бабушками и учителями и т.д., и т.п.
Я не говорю, что надо обязательно Session in view использовать. Но в некоторых случаях это будет меньшее зло.
Здравствуйте, hrensgory, Вы писали:
H>Зачем это всё при наличии возможности сделать transaction per request — H>какие вы видите проблемы в этом подходе?
Транзакция это не сессия, а сессия это не транзакция. Поэтому Session Per Request, а более распространенное название Open Session In View.
Проблема в том что все ассоциации Lazy по-умолчанию. Поэтому вообще все зависимости загружаются лениво из View, порождая огромное количество отдельных запросов. Девелоперы на ранних этапах не обращают на это внимание. А когда это становится проблемой на этапе позднего нагрузочного тестирования, тут-то и становиться ясно, что код не рассчитан на что чтобы указывать FetchMode где либо вообще.
Потому что все сценарии используют одни и те же методы, как предлагает Donz. И добавление FetchMode в один метод влияет сразу на несколько сценариев. Гибкий контроль запросов отсутствует.
H>А какой у него есть выбор кроме EAGER и LAZY? К тому же ЕМНИП сделать H>EAGER более чем одной ассоциированной коллекции (OneToMany, ManyToMany) H>нельзя.
Есть выбор между Lazy, Eager Select (формально, конечно, можно и к Lazy приравнять) и Eager Join.
Есть проблема того что через JOIN нельзя выбрать более одной несортированой коллекции. Но она решается, через введение @OrderBy. И ещё, по-моему, каким-то способом, на вскидку не помню.
H>Там, где имеет смысл реально оптимизировать под каждый сценарий H>(приличная нагрузка + много данных + нельзя использовать 2nd level H>cache) — проще выкинуть ORM вообще и чем раньше тем лучше.
Дело не только в тонкой опимизации запросов, а в том что N+1 происходит повсеместно. Это не "тонкая оптимизация", это жизненая необходимость.
На счет проще выкинуть ORM, я ещё для себя однозначно не решил. С одной стороны — да. Сразу десяток проблем исчезнет. С другой стороны, несколько проблем возникнет, и это только те о которых я знаю сразу. А будет ведь больше.
Здравствуйте, Donz, Вы писали:
D>>>Потому что на момент загрузки сущностей непонятно, нужна эта коллекция или нет. B>>Это аналогично фразе "в данный конкретный момент времени не понятно что должна делать программа". D>Нет, это означает абстрагированность сервисного и DAO слоя от слоя представления данных.
В сервисе мы однозначно должны знать нужна нам коллекция или нет. Иначе у нас View слой напрямую контролирует Persistence. Соответсвтенно управлять Persistence мы тоже можем только из View?
B>>В MVC контроллере и View мы всегда знаем какой у нас происходит сценарий\use case. D>Но не факт, что мы горим желанием на каждый новый сценарий, минорно отличающийся от уже существующих, плодить код в сервисном слое.
Поэтому желательно запланировать простой механизм, как в минорно отличающихся сценариях минорно указать разные планы загрузок сушностей.
D>Вот и не хочется плодить DAO методы, а под них сервисные методы. Изначально хотим достать всех учеников класса. Потом нужно достать учеников со всеми родителями, потом с учителями, потом с учителями и родителями, потом с бабушками и учителями и т.д., и т.п.
Как я уже ответил рядом. Когда у проекта поднимается вопрос в том что в этом заопарке слишком много SELECT запросов получилось, то это решают в лоб — пишут HQL под каждый отдельный сценарий, что и приводит к тому чего изначально хотелось избежать. А можно решить проще. Оставить общий DAO и общие сервисы, просто добавить простой механизм контроля загрузок.
Здравствуйте, Blazkowicz, Вы писали:
B>Потому что все сценарии используют одни и те же методы, как предлагает Donz. И добавление FetchMode в один метод влияет сразу на несколько сценариев. Гибкий контроль запросов отсутствует.
Я это не предлагаю, я говорю, что в некоторых случаях это проще, чем плодить методы на каждый чих.
Здравствуйте, Blazkowicz, Вы писали:
D>>Нет, это означает абстрагированность сервисного и DAO слоя от слоя представления данных. B>В сервисе мы однозначно должны знать нужна нам коллекция или нет. Иначе у нас View слой напрямую контролирует Persistence. Соответсвтенно управлять Persistence мы тоже можем только из View?
Спорно. Мне совершенно не нравится идея, что сервисный слой жестко завязан на вью. Подгрузка необходимых данных без изменения логики — это не сказать, что контроль.
B>А можно решить проще. Оставить общий DAO и общие сервисы, просто добавить простой механизм контроля загрузок.
И как это сделать? Я в свое время не смог придумать. Условие одно — на желание подгрузить еще одну связанную сущность (связь уже присутствует в маппинге) не создавать еще один параметр в сервисном методе или отдельный метод.
Я скорее склоняюсь к тому, что сервисные методы продолжают выполнять исключительно бизнес-сценарии, а контроллер выбирает для вью нужные данные.
> Проблема в том что все ассоциации Lazy по-умолчанию. Поэтому вообще все > зависимости загружаются лениво из View, порождая огромное количество > отдельных запросов.
При отсутствии возможности использования 2nd level cache — да.
> H>А какой у него есть выбор кроме EAGER и LAZY? К тому же ЕМНИП сделать > H>EAGER более чем одной ассоциированной коллекции (OneToMany, ManyToMany) > H>нельзя. > Есть выбор между Lazy, Eager Select (формально, конечно, можно и к Lazy > приравнять) и Eager Join. > Есть проблема того что через JOIN нельзя выбрать более одной > несортированой коллекции. Но она решается, через введение > @OrderBy. И ещё, по-моему, каким-то способом, на вскидку не помню.
Но при этом получатся наверняка монстровые запросы с LEFT OUTER JOIN и
т.п. И опять же — есть аннотация EAGER (ну пусть их 2, ок), есть LAZY —
как вы этим практически управляете "в зависимости от ситуации" (ну кроме
fetch all properties ) ?
> На счет проще выкинуть ORM, я ещё для себя однозначно не решил. С одной > стороны — да. Сразу десяток проблем исчезнет. С другой стороны, > несколько проблем возникнет, и это только те о которых я знаю сразу. А > будет ведь больше.
Здравствуйте, hrensgory, Вы писали:
H>Но при этом получатся наверняка монстровые запросы с LEFT OUTER JOIN и H>т.п.
Лучше один монстровый JOIN чем 100500 отдельных SELECT запросов. К тому же для OneToMany join приходится делать не так часто.
И опять же — есть аннотация EAGER (ну пусть их 2, ок), есть LAZY — H>как вы этим практически управляете "в зависимости от ситуации" (ну кроме H>fetch all properties ) ?
Criteria c = ...
c.setFetchMode("propertyName", FetchMode.JOIN);
c.setFetchMode("propertyName.subPropertyName", FetchMode.JOIN);
Здравствуйте, Blazkowicz, Вы писали:
H>>Но при этом получатся наверняка монстровые запросы с LEFT OUTER JOIN и H>>т.п. B>Лучше один монстровый JOIN чем 100500 отдельных SELECT запросов. К тому же для OneToMany join приходится делать не так часто.
Это не всегда так. Особенно с учетом хибернейтовских кэшей.
Здравствуйте, Donz, Вы писали:
D>Это не всегда так. Особенно с учетом хибернейтовских кэшей.
Это не всегда так, до тех пор пока нет N+1. Когда N+1 вылазит, JOIN почти всегда предпочтительнее.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, hrensgory, Вы писали:
H>>Но при этом получатся наверняка монстровые запросы с LEFT OUTER JOIN и H>>т.п. B>Лучше один монстровый JOIN чем 100500 отдельных SELECT запросов.
Не зарекайтесь. У вас ещё не падал Oracle от многоэтажных join-ов в одном запросе? У нас падал.
Большие запросы требуют больше ресурсов на обработку, поэтому иногда лучше больше несколько простых запросов чем один многоэтажный. А с кэшем второго уровня так вообще ситуация 100% меняется на обратную, потому как эти 100500 select-ов идут по primary key в кэш и зачастую вообще до базы не доходят.
Здравствуйте, Sergey Astakhov, Вы писали:
SA>Не зарекайтесь. У вас ещё не падал Oracle от многоэтажных join-ов в одном запросе? У нас падал. SA>Большие запросы требуют больше ресурсов на обработку, поэтому иногда лучше больше несколько простых запросов чем один многоэтажный. А с кэшем второго уровня так вообще ситуация 100% меняется на обратную, потому как эти 100500 select-ов идут по primary key в кэш и зачастую вообще до базы не доходят.
Это ещё раз показывает что нужно иметь под рукой гибкий механизм контроля FetchMode. Поэтому когда возникает такая проблема всегда можно просто указать, что эту ассоциацию грузить черед SELECT, а не JOIN. А когда этого механизма нет, то его приходится выдумывать, вызывая волну изменений по проекту, потому что один и тот же метод юзается в десятке сценариев без возможности контроля загрузки.
On 05.09.2012 11:08, Blazkowicz wrote:
> SA>Большие запросы требуют больше ресурсов на обработку, поэтому иногда > лучше больше несколько простых запросов чем один многоэтажный. А с кэшем > второго уровня так вообще ситуация 100% меняется на обратную, потому как > эти 100500 select-ов идут по primary key в кэш и зачастую вообще до базы > не доходят.
> Это ещё раз показывает что нужно иметь под рукой гибкий механизм > контроля FetchMode. Поэтому когда возникает такая проблема всегда можно > просто указать, что эту ассоциацию грузить черед SELECT, а не JOIN. А > когда этого механизма нет, то его приходится выдумывать, вызывая волну > изменений по проекту, потому что один и тот же метод юзается в десятке > сценариев без возможности контроля загрузки.
Это понятно, но скажите — вы кэшем (second level) принципиально не
пользуетесь? По-моему это одна из главных фишек ORM.
Здравствуйте, hrensgory, Вы писали:
H>Это понятно, но скажите — вы кэшем (second level) принципиально не H>пользуетесь? По-моему это одна из главных фишек ORM.
Отказ от Open Session In View как связан с отказом от кэша? Правильно. Никакак.