Стратегия получения подмножества коллекции - PullRequest
4 голосов
/ 29 октября 2010

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

Модель данных представляет собой простую связь «один ко многим», например:

<class name="com.foo.Road" table="road">
    <id name="oid" column="oid"/>

    <map name="carCountMap" fetch="subselect">
        <key column="road_oid" foreign-key="oid"/>
        <index column="time_oid" type="long" />
        <one-to-many class="com.foo.CarCount" />
    </map>

    <map name="truckCountMap" fetch="subselect">
        <key column="road_oid" foreign-key="oid"/>
        <index column="time_oid" type="long" />
        <one-to-many class="com.foo.TruckCount" />
    </map>
</class>

Теперь предположим, что данные о количестве легковых и грузовых автомобилей существуют в течение нескольких лет, что намного больше, чем может поместиться в памяти.Кроме того, я действительно заинтересован только в том, чтобы загрузить счетчики автомобилей за последние 3 месяца.

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

  • road.getCarCountMap () возвращает набор только для подсчета количества автомобилей за последние 3 месяца (может быть пустым)
  • Я не получаю какой-то сумасшедший декартово произведение, для обработки которого требуются века
  • Нет LazyInitializationException s выдается после закрытия сессии

Некоторые вещи, которые я пробовал:

1. Сделать carCountMap collection eager и укажите атрибут where в отображении, например:

<map name="carCountMap" fetch="subselect" lazy="false" where="time_oid > 1000"> (аналогично truckCountMap)

Это лучше всего соответствует семантике сбора, которую я хочу,но, к сожалению, это заставляет меня жестко кодировать значение, поэтому я не могу ссылаться на последние 3 месяца.time_oid увеличивается на 1 каждый день.

2. Определите карты как ленивые и используйте hql-запрос, чтобы вручную объединить 3 таблицы:

    from Road r
    left outer join fetch r.carCountMap ccm
    left outer join fetch r.truckCoutnMap tcm
    where (ccm.time.oid > :startDate)
      or (tcm.time.oid > :startDate)

ПроблемаЭто означает, что полученный запрос возвращает несколько миллионов строк, в то время как это должно быть 10 тыс. дорог * 4 измерения в месяц (еженедельно) * 3 месяца = ~ 120 тыс.Этот запрос завершается примерно через час, что нелепо, поскольку подход № 1 (который загружает те же данные, что и я) завершается за 3 минуты.

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

    List roadList = session.createCriteria(Road.class).list();

    session.getNamedQuery("fetchCcm").setLong("startDate", startDate).list();
    session.getNamedQuery("fetchTcm").setLong("startDate", startDate).list();

    return roadList;

Это запускает правильные запросы, но полученные подсчеты легковых и грузовых автомобилей не привязываются кRoad объектов в roadList.Поэтому, когда я пытаюсь получить доступ к счетчикам на любом объекте Road, я получаю LazyInitializationException.

4. Определить карты как ленивые, используйте criteria.list() для загрузки всех дорог, итерируйте повсе даты измерений за последние 3 месяца, поэтому принудительно загрузите эти значения.

Я еще не пробовал, потому что это звучит очень неуклюже, и я не уверен, что избавится от LazyInitializationException

  • Есть ли обходные пути для проблем, с которыми я столкнулся при использовании этих методов?
  • Есть ли вообще лучший метод?

Ответы [ 2 ]

3 голосов
/ 29 октября 2010

Пройдя еще немного, похоже, что фильтры гибернации - это точное решение, которое мне нужно для этого.

Они в основном предоставляют конструкцию, имеющую атрибут where в коллекцииили класс, с параметрами, связанными во время выполнения.

В файле сопоставления определите фильтр и присоедините его к коллекциям:

<class name="com.foo.Road" table="road">
    <id name="oid" column="oid"/>

    <map name="carCountMap" fetch="subselect">
        <key column="road_oid" foreign-key="oid"/>
        <index column="time_oid" type="long" />
        <one-to-many class="com.foo.CarCount" />
        <filter name="byStartDate" condition="time_oid > :startDate" />
    </map>

    <map name="truckCountMap" fetch="subselect">
        <key column="road_oid" foreign-key="oid"/>
        <index column="time_oid" type="long" />
        <one-to-many class="com.foo.TruckCount" />
        <filter name="byStartDate" condition="time_oid > :startDate" />
    </map>
</class>

<filter-def name="byStartDate">
    <filter-param name="startDate" type="long"/>
</filter-def>

Затем в dao включите фильтр, привяжитеПараметр и выполните запрос:

session.enableFilter("byStartDate").setParameter("startDate", calculatedStartDateOid);
return session.createCriteria(Road.class).list();
1 голос
/ 29 октября 2010

Я думаю, что ваша проблема на самом деле состоит из двух частей:

  • Как выразить подмножества данных в модели вашего домена
  • Как получить данные с требуемым уровнем производительности

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

Возможно, было бы лучше сделать отношения между Road s и измерениями трафика однонаправленными, то есть удалить эти карты из класса Road. Это выглядит разумно, поскольку вам, вероятно, не нужны все эти данные сразу. Затем вы можете создать DTO (не отображается!) RoadStatistics, состоящий из Road и этих карт трафика, и заполнить его любыми данными.

Что касается второй части проблемы, я думаю, вам нужно провести несколько экспериментов с чистым SQL, чтобы оптимизировать ваши запросы, а затем перевести оптимальный запрос в HQL или Критерии. Этот перевод может быть легко выполнен, если ваша модель предметной области не ограничивает способ загрузки данных (см. Первую часть). Возможно, вам потребуется оптимизировать схему базы данных, создав несколько индексов и т. Д.

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