Частично заполнить коллекцию детей с помощью NHibernate - PullRequest
6 голосов
/ 07 августа 2009

Я боролся с этим некоторое время, и не могу понять это ...

У меня есть класс BlogPost, который имеет коллекцию Comments, и у каждого комментария есть поле DatePosted.

Что мне нужно сделать, это запросить BlogPost и вернуть его с частично загруженной коллекцией Comments, скажем, все комментарии, опубликованные 1 августа 2009 года.

У меня есть этот запрос:

BlogPost post = session.CreateCriteria<BlogPost>()
    .Add(Restrictions.Eq("Id", 1))
    .CreateAlias("Comments", "c")
    .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1)))
    .UniqueResult<BlogPost>();

Когда я запускаю этот запрос и проверяю сгенерированный sql, он сначала запускает запрос к таблице BlogPost, присоединяется к таблице Comment с правильным ограничением даты, затем запускает второй запрос только на Comment таблица, которая возвращает все.

Результат - коллекция Comments класса BlogPost полностью заполнена!

Что я делаю не так?

У меня есть примеры кода, если кому-то нужна дополнительная информация ...!

Ответы [ 5 ]

2 голосов
/ 17 августа 2009

Для этого есть преобразователь результата, см. документацию.

Цитата:

Обратите внимание, что коллекции котят проводятся экземплярами Cat, возвращенными предыдущие два запроса не являются предварительно отфильтрованы по критериям! если ты хочу получить только котят, которые соответствовать критериям, вы должны использовать SetResultTransformer(CriteriaUtil.AliasToEntityMap).

IList cats =
sess.CreateCriteria(typeof(Cat))
    .CreateCriteria("Kittens", "kt")
        .Add( Expression.Eq("Name", "F%") )
    .SetResultTransformer(CriteriaUtil.AliasToEntityMap)
    .List();

Вы также можете использовать фильтры, которые активируются с помощью session.EnableFilter(name).

Здесь похожий вопрос .

0 голосов
/ 17 августа 2009

Сделайте коллекцию комментариев ленивой, чтобы hibernate не получал ее, когда вы получаете BlogPost. Затем используйте фильтр для коллекции комментариев.

comments = session.CreateFilter(blogPost.Comments, ... ).List();
0 голосов
/ 10 августа 2009

спасибо за ответ, я думаю, я понимаю, почему это так, но я бы подумал, что для этого будет встроенный метод, ваше решение работает, но похоже на хак!

Моя проблема в том, что дочерняя коллекция ОГРОМНА, если не отфильтрована (пример, который я привел для постов и комментариев, был для защиты имен невинных!), И теперь есть способ, которым я могу извлекать все данные каждый раз.

Я запустил Sql Profiler, и он все еще извлекает все данные. когда я запускаю следующий код, первый запрос делает то, что вы ожидаете, только одно сообщение возвращается, но как только второй запрос выполняется, два запроса отправляются в базу данных, первый для получения отфильтрованных комментариев (бинго!), а затем секунду, чтобы заполнить свойство post.Comments всеми комментариями, чего я и пытаюсь избежать!

        var post = session.CreateCriteria<BlogPost>()
            .Add(Restrictions.Eq("Id", 1))
            .UniqueResult<BlogPost>();

        var comments = session.CreateCriteria<Comment>()
            .Add(Restrictions.Eq("BlogPostId", 1))
            .Add(Restrictions.Eq("DatePosted", new DateTime(2009, 8, 1)))
            .List<Comment>();

        post.Comments = comments;

это очень странно, не то, чтобы я перечислял список post.Comments, так почему он заполняет его ?! вот мои классы и карты:

public class BlogPostMap : ClassMap<BlogPost>
{
    public BlogPostMap()
    {
        Id(b => b.Id);
        Map(b => b.Title);
        Map(b => b.Body);
        HasMany(b => b.Comments).KeyColumnNames.Add("BlogPostId");
    }
}
public class CommentMap : ClassMap<Comment>
{
    public CommentMap()
    {
        Id(c => c.Id);
        Map(c => c.BlogPostId);
        Map(c => c.Text);
        Map(c => c.DatePosted);
    }
}

public class BlogPost
{
    public virtual int Id { get; set; }
    public virtual string Title { get; set; }
    public virtual string Body { get; set; }
    public virtual IList<Comment> Comments { get; set; }
}
public class Comment
{
    public virtual int Id { get; set; }
    public virtual int BlogPostId { get; set; }
    public virtual string Text { get; set; }
    public virtual DateTime DatePosted { get; set; }
}

есть идеи?

0 голосов
/ 12 августа 2009

Я согласен, что ручное заполнение коллекции выглядит как хак.

Вместо этого вы можете использовать пользовательский загрузчик. Примерно так:

<query name="loadComments">
<return alias="comments" class="Comment"/>
<load-collection alias="comments" role="Post.comments"/>
from Comments c where c.Id = ? and c.DatePosted = SYSDATE
</query>

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

0 голосов
/ 08 августа 2009

Вы на самом деле не делаете ничего плохого - спящий режим просто не работает таким образом.

Если вы перейдете от BlogPost к комментариям, Hibernate заполнит комментарии на основе указанного сопоставления ассоциации, а не запроса, который вы использовали для получения BlogPost. Предположительно, ваше отображение просто делает соединение на ключевом столбце. Вы можете использовать фильтр , чтобы получить эффект , который вы ищете. Но я думаю, что он все равно получит все комментарии, а затем сделает пост-фильтр.

Проще говоря, просто запросите то, что вы хотите:

List<Comments> comments = session.CreateCriteria<BlogPost>()
            .Add(Restrictions.Eq("Id", 1))
            .CreateAlias("Comments", "c")
            .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1)))
            .list();

Это фактически вернет только комментарии с указанной даты. если вам от этого легче, вы можете установить их так:

post.setComments(comments); //having already retreived the post elsewhere

Меня также удивило это поведение, когда я впервые столкнулся с ним. Это похоже на ошибку, но мне сказали, что это было задумано.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...