Hibernate3 过滤器

发布者:    |       Hibernate ORM

Hibernate3 增加了预定义过滤器条件并能够在类和集合级别附加这些过滤器的能力。什么是“预定义过滤器条件”?嗯,它是指定义类似于在类和各种集合元素上现有的“where”属性的限定子句的能力。但是,这些过滤器条件是可以参数化的!然后,应用程序可以在运行时决定是否启用给定的过滤器以及它们的参数值。

配置

为了使用过滤器,它们必须首先被定义,然后附加到适当的映射元素。要定义一个过滤器,请使用新的<filter-def/>元素在<hibernate-mapping/>元素中

<filter-def name="myFilter">
    <filter-param name="myFilterParam" type="string"/>
</filter-def>

然后,这个过滤器可以附加到一个类

<class name="myClass" ...>
    ...
    <filter name="myFilter" condition=":myFilterParam = my_filtered_column"/>
</class>

或者,到一个集合

<set ...>
    <filter name="myFilter" condition=":myFilterParam = my_filtered_column"/>
</set>

或者,甚至同时附加到两者(或每个的多个)!

用法

为了支持这一点,Hibernate3 添加了一个新的接口 org.hibernate.Filter,并为 org.hibernate.Session 添加了一些新方法。Session 上的新方法有:enableFilter(String filterName)、getEnabledFilter(String filterName) 和 disableFilter(String filterName)。默认情况下,过滤器对于给定会话是禁用的;它们必须通过使用 Session.enabledFilter() 方法显式启用,该方法返回新 Filter 接口的一个实例。使用上面简单定义的过滤器,这看起来可能像这样

session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");

注意,org.hibernate.Filter 接口上的方法允许使用在 Hibernate 中很常见的链式方法。

重要的事情

这都是在 Hibernate 3 版本之前就有的功能,对吧?当然。但在版本 3 之前,这些都是通过应用程序代码的手动过程完成的。要过滤一个集合,你需要加载包含集合的实体,然后将集合应用到 Session.filter() 方法。而对于实体过滤,你必须编写手动修改 HQL 字符串的手动代码或自定义拦截器。

这个新功能提供了一种简洁、一致的方式来应用这些类型的约束。Hibernate 团队设想了该功能在各种场景中的实用性,从国际化到时间数据再到安全考虑(甚至同时结合这些)等等。当然,鉴于到目前为止所使用的简单示例,很难想象该功能的潜在力量,因此让我们看看一些稍微深入的使用方法。

时间数据示例

假设您有一个实体遵循《有效记录》数据库模式。该实体具有多个行,每行基于该记录有效的日期范围而变化(可能甚至通过 Hibernate Interceptor 维护)。雇员记录可能是此类数据的良好示例,因为员工可能会来来去去。此外,假设您正在开发一个始终需要处理雇员当前记录的 UI。为了使用新的过滤器功能实现这些目标,我们首先需要定义过滤器,然后将其附加到我们的 Employee 类。

<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"/>
    ...
    <!--
        Note that this assumes non-terminal records have an eff_end_dt set to a max db date
        for simplicity-sake
    -->
    <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.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
        .setLong("targetSalary", new Long(1000000))
        .list();

在上面的 HQL 中,尽管我们只明确提到了结果上的薪资约束,但由于启用了过滤器,查询将仅返回具有超过一百万美元薪资的当前活跃雇员(幸运儿)。

更进一步,如果从会话中加载了带有《有效日期》过滤器的给定部门,其雇员集合将仅包含活跃雇员。

安全示例

想象一下,我们有一个应用,为每个用户分配一个访问级别,并且系统中的一些敏感实体被分配了访问级别(虽然很简单,但我理解,这只是说明)。因此,用户应该能够看到任何他们的分配访问级别高于他们试图查看的实体分配访问级别的数据。同样,首先我们需要定义过滤器并应用它。

<filter-def name="accessLevel">
    <filter-param name="userLevel" type="int"/>
</filter-def>

<class name="Opportunity" ...>
    ...
    <many-to-one name="region" column="region_id" class="Region"/>
    <property name="amount" type="Money">
        <column name="amt"/>
        <cloumn name="currency"/>
    </property>
    <property name="accessLevel" type="int" column="access_lvl"/>
    ...
    <filter name="accessLevel"><![CDATA[:userLevel >= access_lvl]]></filter>
</class>

<class name="Region" ...>
    ...
    <set name="opportunities" lazy="true">
        <key column="region_id"/>
        <one-to-many class="Opportunity"/>
        <filter name="accessLevel"><![CDATA[:userLevel >= access_lvl]]></filter>
    </set>
    ...
</class>

接下来,我们的应用程序代码需要启用过滤器。

User user = ...;
Session session = ...;
session.enableFilter("accessLevel").setParameter("userLevel", user.getAccessLevel());

此时,加载一个 Region 会根据当前用户的访问级别过滤其机会集合。

Region region = (Region) session.get(Region.class, "EMEA");
region.getOpportunities().size(); // <- limited to those accessible by the user's level

结论

这些都是相当简单的示例。但希望它们能给您展示这些过滤器有多强大,也许激发了一些关于您如何在应用程序中应用此类约束的想法。与各种拦截方法(如 Web 过滤器等)结合使用时,这可以变得更加强大。另外,请注意:如果您计划使用带有外部连接的过滤器(无论是通过 HQL 还是加载获取),请小心条件表达式的方向。最安全的方法是为左外部连接设置此设置;通常,将参数首先放在操作符之后,然后是列名(s)。


返回顶部