Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности

Новые сообщения [новые:0]
Дайджест
Горячие темы
Избранное [новые:0]
Форумы
Пользователи
Статистика
Статистика нагрузки
Мод. лог
Поиск
|
|
03.02.2011, 11:02
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
Всем доброго времени суток. Вопрос вот такой вот. Решил оптимизировать работу с деревом категорий. С одной стороны, хотелось бы для отдельных случаев оставить возможность работать с деревом через ленивую загрузку. Но в ситуациях с редактированием деревьев нужно загружать их полностью. Самый простой вариант, который пришел, выключить ленивую загрузку подкатегорий и установить batch="очень много", тогда запросов на БД (при загрузке деревьев категорий) идет очень мало. Но при этом возможность работать лениво (даже при установке в запросах _session.QueryOver<Category>().Where(category => category.Id == id).Fetch(x => x.ChildCategories).Lazy ) пропадает... Таким образом, подумал, LazyLoad отключать нельзя, но нужно попробовать загрузить все категории одним запросом, прописанным на TSQL вручную. Нашел статью , в которой говорится, что есть такая возможность в NHibernate. Обрадовался, реализовал: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. Метод GetFullTree реально инициализирует громадную коллекцию объектов, и возвращает root клиентскому коду. Но при первом обращении клиентского кода к любому rootCategory.ChildCategories[x], на базу данных уходит запрос на получение этого Child'а. Это, видимо, потому, что NHibernate-то создал кучу объектов при TSQL-запросе, но не связал их, и обернул проксиками. Таким образом проблема: lazy отключать нельзя, потому, что из кода его включить не удается, а загружать через custom-tsql нельзя, так как объекты не связываются друг с другом, оборачиваются в прокси, и при первом обращении к подобъектам будут сгенерированы запросы на базу данных. Игрался уже с second-level cache Код: plaintext 1. 2. 3. 4. Код: plaintext 1. 2. 3. 4. 5. ...не помогло, так как, по-видимому, он кеширует именно запросы, а TSQL-запрос сильно отличается от тех, что генерит NHibernate. Подскажите, как тут быть? Спасибо :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
03.02.2011, 16:27
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
Если я не понятно сформулировал вопрос, скажите пожалуйста, я переформулирую. Спасибо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 08:46
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
Pregamil, да нахрен эти нибернейты нужны? у них ж ума нету ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 08:49
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
ViPRosда нахрен эти нибернейты нужны? у них ж ума нету Если к NHibernate с умом подойти, то очень нужны. А по теме есть какие-нибудь идеи? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 08:57
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
Pregamil, идея одна - простая в БД нафиг не нужны деревя, потому что Родитель и Потомок разного типа (попроще, из разных таблиц), так что имеем n типов и их агрегатов (юнион) и скоко надо ограничений - Может состоять(агрегатный типI, агрегатный тип J,....) одноуревновые и отношений Состоит(элементагрегатный типI, элементагрегатный тип J,....) мозгов у хибера не хватит выбрать их по уму ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 09:17
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
ViPRosPregamil, идея одна - простая в БД нафиг не нужны деревя, потому что Родитель и Потомок разного типа (попроще, из разных таблиц), так что имеем n типов и их агрегатов (юнион) и скоко надо ограничений - Может состоять(агрегатный типI, агрегатный тип J,....) одноуревновые и отношений Состоит(элементагрегатный типI, элементагрегатный тип J,....) мозгов у хибера не хватит выбрать их по уму Ну, во-первых, в моём случае родитель и потомок одного типа (попроще, в одной таблице живут). Во-вторых, если имеем n типов, то не факт, что агрегатов будет тоже n, по крайней мере не всегда так. В NHibernate для выборки таких связанных коллекций можно указать Fetch(x=>x.Collection).Eager, тогда он выберет подколлекцию сразу же, через left outer join. С выборками из разных таблиц, в общем-то, более-менее понятно, но здесь имеем дело с одной таблицей и рекурсивной выборкой всего дерева. Fetch(x=>x.Collection).Eager выбирает только один подуровень (коллекцию Children первого уровня). Но нужно всё дерево. Можно отключить ленивую загрузку и установить batchsize="много", тогда он сделает пару-тройку запросов на базу, и выберет всё дерево, но как тогда включить lazyLoading для других запросов? Из кода включить не удалось. Далее, попалась под руку статья, где указывается, что можно самому прописать рекурсивный TSQL-запрос, и подсунуть хиберу в session.CreateSQLQuery. Прописал, подсунул, получил несколько тысяч категорий в коллекции. Но друг с другом они не связаны, и в каждой категории Children'ы обёрнуты в проксики, поэтому при первом обращении к любой коллекции сразу же следует вызов на БД. А россыпью мне эти несколько тыщ категорий не нужны. Какие идеи? (кроме реализации на чистом ado.net) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 10:04
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
Pregamil, во первых :) я не сказал что будет n агрегатов, скорееих будет >= n так как типы сами выступают в роли агрегата тоже во вторых, неужто этот batchsize действует на всю сессиию, скорее есть возможность указать это для отдельного графа (подсхемы) вощем это все теория, счас пацаны проснуться и скажут ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 11:20
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
ViPRosсчас пацаны проснуться и скажут Проснулся, вот ответ . ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
04.02.2011, 15:32
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
SolYUtorViPRosсчас пацаны проснуться и скажут Проснулся, вот ответ . Здорово, работает :) Спасибо! Опробовал этот вариант, вот код: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. И вот SQL, который пошел на базу данных при выполнении этого метода: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. В результате получил проинициализированное дерево. Только есть одно но: вне сессии его использовать нельзя, так как Children-коллекции листьев этого дерева (которые, по сути, должны быть пустыми коллекциями), NHibernate оформил в виде своих проксиков, которые, опять таки, ломятся в базу. Можно-ли каким-то образом заставить его генерировать пустые коллекции, которые при первом доступе к ним не будут ломиться в базу? Спасибо. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
07.02.2011, 12:03
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
Pregamil, Вы немного не то делаете. Надо грузить в список все категории, а уже корневые выбирать из этого загруженного списка. Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. В результатет ни одного дополнительного запроса в базу: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
08.02.2011, 16:06
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
SolYUtorВы немного не то делаете. Надо грузить в список все категории, а уже корневые выбирать из этого загруженного списка. Спасибо, проверил. И что самое интересное: если идти по алгоритму: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. т.е. в одной сессии и создать, и прочитать категории, то вне сессии (после её закрытия), всё отрабатывает по-человечески, без проксиков и поползновений в базу. Но если сохранить дерево в одной сессии, а прочитать в совершенно другой, то NHibernate тут же обернет чайлдов категорий-листьев дерева в проксики, и попытается полезть в базу. Написал тестовый пример. SQL базы: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. Вывод на консоль в рамках одной сессииNHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SC OPE_IDENTITY();@p0 = 'Parent' [Type: String (4000)], @p1 = NULL [Type: Int32 (0) ] NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child1' [Type: String (4000)], @p1 = 33 [Type: Int32 (0)] NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child2' [Type: String (4000)], @p1 = 33 [Type: Int32 (0)] NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'SubChild' [Type: String (4000)], @p1 = 35 [Type: Int32 (0) ] NHibernate: select category0_.Id as Id0_0_, children1_.Id as Id0_1_, category0_.Name as Name0_0_, category0_.ParentId as ParentId0_0_, children1_.Name as Name0_1_, children1_.ParentId as ParentId0_1_, children1_.ParentId as ParentId0__, children1_.Id as Id0__ from Categories category0_ inner join Categories children1_on category0_.Id=children1_.ParentId ---- 33 - Parent 34 - Child1 35 - Child2 36 - SubChild Вывод на консоль в рамках разных сессийNHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Parent' [Type: String (4000)], @p1 = NULL [Type: Int32 (0)] NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child1' [Type: String (4000)], @p1 = 25 [Type: Int32 (0)] NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Child2' [Type: String (4000)], @p1 = 25 [Type: Int32 (0)] NHibernate: INSERT INTO Categories (Name, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'SubChild' [Type: String (4000)], @p1 = 27 [Type: Int32 (0)] NHibernate: select category0_.Id as Id0_0_, children1_.Id as Id0_1_, category0_.Name as Name0_0_, category0_.ParentId as ParentId0_0_, children1_.Name as Name0_1_, children1_.ParentId as ParentId0_1_, children1_.ParentId as ParentId0__, chi ldren1_.Id as Id0__ from Categories category0_ inner join Categories children1_on category0_.Id=children1_.ParentId ----- 25 - Parent 26 - Child1 NHibernate: SELECT children0_.ParentId as ParentId1_, children0_.Id as Id1_, children0_.Id as Id0_0_, children0_.Name as Name0_0_, children0_.ParentId as ParentId0_0_ FROM Categories children0_ WHERE children0_.ParentId=@p0;@p0 = 26 [Type:Int32 (0)] 27 - Child2 28 - SubChild NHibernate: SELECT children0_.ParentId as ParentId1_, children0_.Id as Id1_, children0_.Id as Id0_0_, children0_.Name as Name0_0_, children0_.ParentId as ParentId0_0_ FROM Categories children0_ WHERE children0_.ParentId=@p0;@p0 = 28 [Type:Int32 (0)] Контрольный пример приложил в аттаче. Параметр метода SessionManager.OpenSession(true/false) меняет видимость сессии (возвращает новую сессию, или старую, уже использованую ранее). Заранее благодарен, если есть идет, почему так происходит и как с этим работать. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
08.02.2011, 17:16
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
Pregamil, NHibernate всегда старается грузить как можно меньше данных из бд. Рецепт стар как мир - ленивая загрузка. Поэтому ему и нужны прокси - для автоматической ленивой загрузки, без дополнительных телодвижений. Сессия всегда возвращает прокси-объекты (если включена ленивая загрузка). Даже когда вы грузите всё дерево целиком с DistinctRootEntityResultTransformer, всё равно возвращаются прокси, только уже инициализированные. Прокси-объект всегда связан с той сессией, в которой он открыт, потому после закрытия сессии становится почти безполезен. Использование сессии для одной операции, и последующие ее закрытие (как в вашем примере) - это антишаблон. Если вы работаете с деревом - то держите сессию открытой, и уже по окончании работы с деревом сбрасывайте изменения в БД ( Session.Flush() ). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
08.02.2011, 17:35
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
SolYUtorЕсли вы работаете с деревом - то держите сессию открытой, и уже по окончании работы с деревом сбрасывайте изменения в БД ( Session.Flush() ). Изменять категории мне не нужно, нужно только читать. Как в принципе работать с сессиями и NH мне понятно. Задача, в общем-то, немного другая: в приложении выгрузить все категории и отобразить на странице. Выгрузить одним запросом. Т.е. в базе уже есть категории, и если выгрузить их одним запросом и начать рекурсивно конвертировать автомаппером на [Category -> CategoryViewModel], то автомаппер будет обходить всё дерево. И когда дойдет до листьев, NH будет делать запросы на БД из-за проксиков. Чтобы всё работало и не падало, надо держать сессию открытой, тогда при доступе автомаппера к самым конечным подкатегориям он отправит запрос на базу. В итоге, при выборе всех категорий NH шлет один запрос на базу, и при конвертировании категории в CategoryViewModel будет еще несколько запросов на базу (в примере выше - +2 запроса, из-за Lazy Loading-а). Категорий много, поэтому мелких запросов на сервер больше тысячи, что убивает производительность. Да и попытка выкачать все категории одним махом ради одного запроса на базу и была. Если выключить LazyLoading, то не получится работать с категориями лениво. Вот и вопрос: каким именно образом выгрузить категории из базы и сконвертировать их в иерархию других классов (CategoryViewModel), чтобы при этом пошел ровно один запрос на базу данных, а не 1001 (при конвертировании)? Согласен и сессию держать открытой, и что угодно. Но как? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
08.02.2011, 17:50
|
|||
|---|---|---|---|
NHibernate - loading trees |
|||
|
#18+
Pregamil, речь идёт об ASP.NET приложении? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
08.02.2011, 17:57
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
да, об ASP.NET MVC. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
09.02.2011, 12:08
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
Практически всё правильно, за исключением одной маленькой ошибки: session.CreateQuery("from Category cat left join fetch cat.Children") Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Таким образом при закрытой сессии можно работать со всем деревом, в базу больше лезть не будет. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
09.02.2011, 13:13
|
|||
|---|---|---|---|
|
|||
NHibernate - loading trees |
|||
|
#18+
А вот, по сути, и разница: Первый вариант: from Category cat inner join fetch cat.Children Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. SQL output: Id0_0Id0_1Name0_0ParentId0_0Name0_1ParentId0_0ParentId_0Id0_12ParentNULLChild111213ParentNULLChild211334Child21SubChild334 Второй вариант: from Category cat left join fetch cat.Children Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. SQL output: Id0_0Id0_1Name0_0ParentId0_0Name0_1ParentId0_0ParentId_0Id0_12ParentNULLChild111213ParentNULLChild21132NULLChild11NULLNULLNULLNULL34Child21SubChild3344NULLSubChild3NULLNULLNULLNULL Поэтому при inner-join при доступе к самым вложенным категориям NHibernate лезет в базу, так как у него нет информации о Child-ах: есть они или нет. При left outer join лезть в базу смысла нет, так как он теперь явно понимает, что коллекция пуста. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|

start [/forum/topic.php?fid=17&mobile=1&tid=1350902]: |
0ms |
get settings: |
7ms |
get forum list: |
10ms |
check forum access: |
2ms |
check topic access: |
2ms |
track hit: |
52ms |
get topic data: |
6ms |
get forum data: |
1ms |
get page messages: |
28ms |
get tp. blocked users: |
1ms |
| others: | 214ms |
| total: | 323ms |

| 0 / 0 |
