<?xml version="1.0" encoding="UTF-8"?>

<!--
  ~ Hibernate, Relational Persistence for Idiomatic Java
  ~
  ~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
  ~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
  -->
<chapter xml:id="filters" xmlns="http://docbook.org/ns/docbook" >
  <title>Filtering data</title>

  <para>Hibernate provides an innovative new approach to handling data with
  "visibility" rules. A <emphasis>Hibernate filter</emphasis> is a global,
  named, parameterized filter that can be enabled or disabled for a particular
  Hibernate session.</para>

  <section xml:id="objectstate-filters" revision="1">
    <title>Hibernate filters</title>

    <para>Hibernate has the ability to pre-define filter criteria and attach
    those filters at both a class level and a collection level. A filter
    criteria allows you to define a restriction clause similar to the existing
    "where" attribute available on the class and various collection elements.
    These filter conditions, however, can be parameterized. The application
    can then decide at runtime whether certain filters should be enabled and
    what their parameter values should be. Filters can be used like database
    views, but they are parameterized inside the application.</para>

    <para>Using annotatons filters are defined via
    <literal>@org.hibernate.annotations.FilterDef</literal> or
    <literal>@org.hibernate.annotations.FilterDefs</literal>. A filter
    definition has a <methodname>name()</methodname> and an array of
    parameters(). A parameter will allow you to adjust the behavior of the
    filter at runtime. Each parameter is defined by a
    <literal>@ParamDef</literal> which has a name and a type. You can also
    define a <methodname>defaultCondition()</methodname> parameter for a given
    <literal>@FilterDef</literal> to set the default condition to use when
    none are defined in each individual <literal>@Filter</literal>.
    <literal>@FilterDef</literal>(s) can be defined at the class or package
    level. </para>

    <para>We now need to define the SQL filter clause applied to either the
    entity load or the collection load. <literal>@Filter</literal> is used and
    placed either on the entity or the collection element. The connection
    between <classname>@FilterName</classname> and
    <classname>@Filter</classname> is a matching name.</para>

    <example>
      <title>@FilterDef and @Filter annotations</title>

      <programlisting language="JAVA" role="JAVA">@Entity
@FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) )
@Filters( {
    @Filter(name="betweenLength", condition=":minLength &lt;= length and :maxLength &gt;= length"),
    @Filter(name="minLength", condition=":minLength &lt;= length")
} )
public class Forest { ... }</programlisting>
    </example>

    <para>When the collection use an association table as a relational
    representation, you might want to apply the filter condition to the
    association table itself or to the target entity table. To apply the
    constraint on the target entity, use the regular
    <literal>@Filter</literal> annotation. However, if you want to target the
    association table, use the <literal>@FilterJoinTable</literal>
    annotation.</para>

    <example>
      <title>Using <classname>@FilterJoinTable</classname> for filterting on
      the association table</title>

      <programlisting language="JAVA" role="JAVA">@OneToMany
@JoinTable
//filter on the target entity table
@Filter(name="betweenLength", condition=":minLength &lt;= length and :maxLength &gt;= length")
//filter on the association table
@FilterJoinTable(name="security", condition=":userlevel &gt;= requredLevel")
public Set&lt;Forest&gt; getForests() { ... }</programlisting>
    </example>

    <para>By default, Hibernate attempts to automatically determine all points within the
    <literal>@Filter</literal> SQL condition fragment that an alias should be injected.  To control the alias injection,
    set <literal>deduceAliasInjectionPoints</literal> to <literal>false</literal> within the
    <literal>@Filter</literal>.  Injection points are then marked using <literal>@SqlFragmentAlias</literal> annotations or
    within the SQL's condition fragment using <literal>{alias}</literal>.</para>
    
    <para>In addition to allowing explicit alias control, <literal>deduceAliasInjectionPoints</literal>
    provides an out when Hibernate assumes an ANSI SQL reserved keyword is a column and incorrectly aliases it.</para>

	<example>
		<title>@Filter annotation, disabling deduceAliasInjectionPoints</title>

		<programlisting language="JAVA" role="JAVA">@Entity
@Table(name="T_TREE")
@Filters({
	@Filter(name="isTall", condition="{alias}.LENGTH &gt;= 100", deduceAliasInjectionPoints = false),
	@Filter(name="isOak", condition="{t}.WOODTYPE like 'oak'", deduceAliasInjectionPoints = false,
		aliases={@SqlFragmentAlias(alias="t", table="T_TREE")})
})
public class Tree { ... }
		</programlisting>
	</example>

    <para>Using Hibernate mapping files for defining filters the situtation is
    very similar. The filters must first be defined and then attached to the
    appropriate mapping elements. To define a filter, use the
    <literal>&lt;filter-def/&gt;</literal> element within a
    <literal>&lt;hibernate-mapping/&gt;</literal> element:</para>

    <example>
      <title>Defining a filter definition via
      <literal>&lt;filter-def&gt;</literal></title>

      <programlisting role="XML">&lt;filter-def name="myFilter"&gt;
    &lt;filter-param name="myFilterParam" type="string"/&gt;
&lt;/filter-def&gt;</programlisting>
    </example>

    <para>This filter can then be attached to a class or collection (or, to
    both or multiples of each at the same time):</para>

    <example>
      <title>Attaching a filter to a class or collection using
      <literal>&lt;filter&gt;</literal></title>

      <programlisting role="XML">&lt;class name="myClass" ...&gt;
    ...
    &lt;filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/&gt;

    &lt;set ...&gt;
        &lt;filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/&gt;
    &lt;/set&gt;  
&lt;/class&gt;</programlisting>
    </example>

    <para>The methods on <literal>Session</literal> are:
    <literal>enableFilter(String filterName)</literal>,
    <literal>getEnabledFilter(String filterName)</literal>, and
    <literal>disableFilter(String filterName)</literal>. By default, filters
    are <emphasis>not</emphasis> enabled for a given session. Filters must be
    enabled through use of the <literal>Session.enableFilter()</literal>
    method, which returns an instance of the <literal>Filter</literal>
    interface. If you used the simple filter defined above, it would look like
    this:</para>

    <programlisting role="JAVA">session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");</programlisting>

    <para>Methods on the org.hibernate.Filter interface do allow the
    method-chaining common to much of Hibernate.</para>

    <para>The following is a full example, using temporal data with an
    effective record date pattern:</para>

    <programlisting role="XML">&lt;filter-def name="effectiveDate"&gt;
    &lt;filter-param name="asOfDate" type="date"/&gt;
&lt;/filter-def&gt;

&lt;class name="Employee" ...&gt;
...
    &lt;many-to-one name="department" column="dept_id" class="Department"/&gt;
    &lt;property name="effectiveStartDate" type="date" column="eff_start_dt"/&gt;
    &lt;property name="effectiveEndDate" type="date" column="eff_end_dt"/&gt;
...
    &lt;!--
        Note that this assumes non-terminal records have an eff_end_dt set to
        a max db date for simplicity-sake
    --&gt;
    &lt;filter name="effectiveDate"
            condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/&gt;
&lt;/class&gt;

&lt;class name="Department" ...&gt;
...
    &lt;set name="employees" lazy="true"&gt;
        &lt;key column="dept_id"/&gt;
        &lt;one-to-many class="Employee"/&gt;
        &lt;filter name="effectiveDate"
                condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/&gt;
    &lt;/set&gt;
&lt;/class&gt;</programlisting>

    <para>In order to ensure that you are provided with currently effective
    records, enable the filter on the session prior to retrieving employee
    data:</para>

    <programlisting role="JAVA">Session session = ...;
session.enableFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary &gt; :targetSalary")
         .setLong("targetSalary", new Long(1000000))
         .list();
</programlisting>

    <para>Even though a salary constraint was mentioned explicitly on the
    results in the above HQL, because of the enabled filter, the query will
    return only currently active employees who have a salary greater than one
    million dollars.</para>

    <para>If you want to use filters with outer joining, either through HQL or
    load fetching, be careful of the direction of the condition expression. It
    is safest to set this up for left outer joining. Place the parameter first
    followed by the column name(s) after the operator.</para>

    <para>After being defined, a filter might be attached to multiple entities
    and/or collections each with its own condition. This can be problematic
    when the conditions are the same each time. Using
    <literal>&lt;filter-def/&gt;</literal> allows you to definine a default
    condition, either as an attribute or CDATA:</para>

    <programlisting role="XML">&lt;filter-def name="myFilter" condition="abc &gt; xyz"&gt;...&lt;/filter-def&gt;
&lt;filter-def name="myOtherFilter"&gt;abc=xyz&lt;/filter-def&gt;</programlisting>

    <para>This default condition will be used whenever the filter is attached
    to something without specifying a condition. This means you can give a
    specific condition as part of the attachment of the filter that overrides
    the default condition in that particular case.</para>
  </section>
</chapter>
