Глава 19. Фильтрация данных
Оглавление- 19.1. Фильтры Hibernate
Hibernate предоставляет инновационный подход к обработке данных с помощью правил «видимости». Фильтр Hibernate является глобальным, именованным, параметризированным фильтром, который может быть включён или отключён для определенной сессии Hibernate.
19.1. Фильтры Hibernate
Hibernate имеет возможность предварительно определить критерии фильтра и присоединить эти фильтры как на уровне класса, так и на уровне коллекции. Критерии фильтра позволяют вам определить секцию ограничения, подобное существующему атрибуту «where», доступному для класса и различных элементов коллекции. Однако эти условия фильтра могут быть параметризованы. Затем приложение может решить во время работы, должны ли быть включены определенные фильтры и каковы должны быть их значения параметров. Фильтры могут использоваться как представления базы данных, но они параметризуются внутри приложения.
Использование аннотаций фильтров определяется через @org.hibernate.annotations.FilterDef
или @org.hibernate.annotations.FilterDefs. Определение фильтра имеет имя name()
и массив параметров. Параметр позволяет настроить поведение фильтра во время выполнения.
Каждый параметр определяется @ParamDef, который имеет имя и тип. Вы также можете
определить параметр defaultCondition() для данного @FilterDef, чтобы установить
условие по умолчанию, которое будет использоваться, если в каждом отдельном @Filter
не определены. @FilterDef() могут быть определены на уровне класса или пакета.
Теперь нам нужно определить секцию SQL-фильтра, применяемое к загрузке сущности или загрузке
к коллекции. @Filter используется и помещается либо на сущность, либо
на элемент коллекции. Соединение между @FilterName и @Filter является
совпадающим именем.
Пример 19.1. Аннотации @FilterDef и @Filter
@Entity
@FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) )
@Filters( {
@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
@Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
Когда коллекция использует таблицу ассоциаций в качестве реляционного представления, вы можете
применить условие фильтра к самой таблице ассоциаций или к таблице целевых сущностей. Чтобы применить
ограничение к целевой сущности, используйте стандартную аннотацию @Filter. Однако, если
ваша цель - таблица ассоциаций, используйте аннотацию @FilterJoinTable.
Пример 19.2. Использование @FilterJoinTable для фильтрации в таблице ассоциаций
@OneToMany @JoinTable //filter on the target entity table @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length") //filter on the association table @FilterJoinTable(name="security", condition=":userlevel >= requredLevel") public SetgetForests() { ... }
По умолчанию Hibernate пытается автоматически определить все точки внутри фрагмента условия
@Filter, в которые должен быть введен псевдоним. Чтобы управлять внедрением псевдонима,
установите значение deduceAliasInjectionPoints в false в @Filter.
Точки внедрения затем помечаются с помощью аннотаций @SqlFragmentAlias или в фрагменте
условия SQL с использованием {alias}.
В дополнение к разрешению явного управления псевдонимами, deduceAliasInjectionPoints
предоставляет выход, когда Hibernate предполагает, что зарезервированное ANSI SQL ключевое слово является
столбцом и неправильно связывает его с псевдоним.
Пример 19.3. Аннотации @Filter, отключение
deduceAliasInjectionPoints
@Entity
@Table(name="T_TREE")
@Filters({
@Filter(name="isTall", condition="{alias}.LENGTH >= 100", deduceAliasInjectionPoints = false),
@Filter(name="isOak", condition="{t}.WOODTYPE like 'oak'", deduceAliasInjectionPoints = false,
aliases={@SqlFragmentAlias(alias="t", table="T_TREE")})
})
public class Tree { ... }
С использованием файлов отображения Hibernate для определения фильтров ситуация очень похожая. Сначала фильтры должны быть определены, а затем привязаны к соответствующим элементам отображения. Чтобы определить фильтр, используйте элемент <filter-def /> внутри элемента <hibernate-mapping />:
Пример 19.4. Определение фильтра через <filter-def>
<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>
Затем этот фильтр можно присоединить к классу или коллекции (или к обоим или многократно к каждому одновременно):
<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>
</class>
Методами Session являются: enableFilter(String filterName),
getEnabledFilter(String filterName) и disableFilter(String filterName).
По умолчанию фильтры не включены для данной сессии. Фильтры должны быть активированы
с помощью метода Session.enableFilter(), который возвращает экземпляр интерфейса
Filter. Если вы использовали простой фильтр, определённый выше, он будет выглядеть так:
session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
Методы интерфейса org.hibernate.Filter позволяют создать цепочку методов (method-chaining)
с большинством методов Hibernate.
Ниже приведен полный пример использования временных данных с эффективным шаблоном даты записи:
<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
<class name="Employee" ...>
...
<many-to-one name="department" column="dept_id" class="Department"/>
<property name="effectiveStartDate" type="date" column="eff_start_dt"/>
<property name="effectiveEndDate" type="date" column="eff_end_dt"/>
...
<!--
Обратите внимание, что это предполагает, что, для упрощения, нетерминальные записи имеют значение
eff_end_dt установленое в максимальную дату db
-->
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>
<class name="Department" ...>
...
<set name="employees" lazy="true">
<key column="dept_id"/>
<one-to-many class="Employee"/>
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</set>
</class>
Чтобы убедиться, что вам предоставлены действующие записи, включите фильтр на сессии до получения данных о сотрудниках:
Session session = ...;
session.enableFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
Несмотря на то, что ограничение на зарплату было указано явно в вышеупомянутом HQL, из-за включенного фильтра запрос будет возвращать только текущих активных сотрудников, у которых зарплата превышает один миллион долларов.
Если вы хотите использовать фильтры с внешним соединением (outer join), либо через HQL, либо при загрузке выборки, будьте осторожны с направлением выражения условия. Это безопаснее установить для left outer join. Сначала поместите параметр, а затем имя столбца после оператора.
После определения фильтр может быть присоединен к нескольким сущностям и/или коллекциям, каждый со своим собственным условием. Это может быть проблематично, когда каждый раз условия одинаковы. Использование <filter-def /> позволяет определить условие по умолчанию либо как атрибут, либо CDATA:
<filter-def name="myFilter" condition="abc > xyz">...</filter-def> <filter-def name="myOtherFilter">abc=xyz</filter-def>
Это условие по умолчанию будет использоваться всякий раз, когда фильтр привязан к чему-либо без указания условия. Это означает, что вы можете указать конкретное условие как часть прикрепления фильтра, который переопределяет условие по умолчанию в этом конкретном случае.