Глава 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>
Это условие по умолчанию будет использоваться всякий раз, когда фильтр привязан к чему-либо без указания условия. Это означает, что вы можете указать конкретное условие как часть прикрепления фильтра, который переопределяет условие по умолчанию в этом конкретном случае.