在很多应用程序中不需要一次使用数据表中的所有的数据。在这种情况下,我们就需要使用hibernate的过滤器(filter)来得到一个数据字集。过滤器的主要作用是限制我们的应用程序的查询数据。这个过滤器的概念并不是什么新概念,如数据库的视图也属于过滤器范畴。而hibernate为我们提供的过滤器是在一个抽象的层次,它可以做到和不依赖于数据库的类型,使数据访问层只面对一个解决方案,那就是hibernate filter。当然,直接在数据库中建立视图也算一种解决方案,但这样做太不灵活。而hibernate过滤器却能够在hibernate会话的过程中打开或关闭。另外,另外,hibernate过滤器可以传递参数,这样将大大增加hibernate的灵活性。虽然hibernate2也提供了过滤器,但hibernate3.x提供了一种全新的过滤器。
hibernate3的过滤器可以进行预定义,并可绑定在类和集合层。那么什么叫预定义过滤器呢?就是可以定义象"where"子句的限制性子句,但这些子句是相对类和不同集合的元素的。除了这些过滤器条件可以被参数化外。系统还能决定在运行是指定的过滤器是否应该被打开以及什么值被传入过滤器参数。
一、什么时候使用过滤器
为了解释为什么使用过滤器,下面让我们先来看一个例子。假设有一个管理用户信息的web应用程序。在当前状态,我们的应用程序使用同一个应用接口来处理所有的用户信息。但如果最终用户要求将活动用户和过期用户分开管理。这些状态信息被保存在用户表的某一列中。对于这种需求,我们最容易想到的一个解决方案是重新写每一个select hql查询语句,也就是在每一个hql后加一个where条件来过滤这些数据。当然,这种方法的复杂程序取决于你的web程序是如何建立的,可以很简单,也可以很复杂。但不管是简单还是复杂,都必须得修改我们曾经测试过的代码,这将给我们的程序带来非常大的隐患。而使用hibernate3.x,将会给我们带来另外一个解决方案。我们将会为应用程序的用户状态创建一个过滤器。当用户选择用户的状态时(活动或过期),应用程序将激活当前hibernate会话的用户状态过滤器。这样以来,所有的select hql查询将返回查询结果的子集,而我们只需要在hibernate会话状态和用户状态过滤器两个地方添加代码,并不需要修改原先的代码。
从概念上讲,你可以使用where子句在应用程序中完成和hibernate过滤器同样的工作。当然,我们还可以在数据库中建立视图来完成同样的工作(但所使用的数据库必须支持视图功能)。这三种解决方案都可以通过一个或多个查询条件来限制最终结果。而hibernate过滤器的优势就在于可以随时在程序中关闭或打开,也就是说过滤器是可编程的,而且过滤器被定义在hibernate的映射文件中,这样将非常容易维护。当然,过滤器也有不足的地方,主要的不足就是在运行时无法创建新的过滤器。而程序在运行时,所有的过滤器必须要在映射文件中被指定。虽然这将大大限制过滤器的灵活性,但过滤器支持参数化。对于本文的例子,我们可以在映射文件中指定保存用户状态信息的列。我们不需要在映射文件中指定可能的状态值,这些在程序运行时都可以指定。接下来我们就来看看如何使用hibernate3.x中的过滤器来写程序。 二、过滤器的配置
为了使用过滤器,它们需要首先被定义,然后和响应的元素绑定。为了定义一个过滤器,我们将使用<hibernate-mapping/>中的<filter-def/>元素来定义hibernate过滤器。如下所示:
| <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.x中新添加了一个接口:org.hibernate.filter,以及在org.hibernate.session中加入了一些新方法。在session中的新方法有:enablefilter(string filtername), getenabledfilter(string filtername),以及disablefilter(string filtername)。在默认性况下,对于当前的会话,过滤器是关闭的。它们必须使用session的enablefilter()方法来显式地打开过滤器。这个方法返回一个新过滤器的实例。按着上述的做法,我们可以用如下的代码打开过滤器:
| session.enablefilter("myfilter").setparameter("myfilterparam", "some-value"); |
过滤大数据集
对于hibernate3.x以前的版本,处理大数据集必须要使用大量的代码。为了达到目的,必须先将整个数据集装到内存中,然后对这个数据集使用session的filter()方法来过滤。而且当过滤实体时,我们必须手工写全部的hql或一个定制的拦截器。
而在hibernate3.x中提供了一种更简捷,更一致的方法来过滤数据。hibernate设计街头团队想用一个更有效的特性来处理每一件事,无论它们是国际化的还是本地数据,或是为了安全考虑以及其他的事。现在让我们看一个例子来解释这一切。 四、实例
1. 一个过滤数据的例子
现在假设有一个实体,这个实体跟着“有效的记录”数据库模式。这个实体有多个行,每一行都根据日期不同而不同,也就是说在日期范围内是有效的。一个employment记录将是一个非常好的例子,因为employees可以来或去或再回来。现在我们开发一个带ui的程序,这个程序需要处理employment数据的当前记录。为了使用新的过滤器特性达到这个目的。我们首先需要定义这个过滤器,然后将它附着在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"/> ... <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中,即使我们只给出一个salary约束条件,由于我们已经打开发过滤器,也只会得到当前活动的比一百万多的雇员。
2. 安全的实例
假设我们有一个应用程序,这个程序给每一个用户分配一个访问权限。在这其间,系统中一些敏感的实体被分配给了某些访问层次。因此,一个用户在它所属的访问层中应该可以看到更多的东西。在本例子中我们就要通过过滤器来过滤某一个访问层次中的实体。下面让我们来定义过滤器。
| <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">= access_lvl]]> </class> <class name="region" ...> ... <set name="opportunities" lazy="true"> <key column="region_id"/> <one-to-many class="opportunity"/> <filter name="accesslevel">= access_lvl]]> </set> ... </class> |
接下来,让我们来打开过滤器。
| user user = ...; session session = ...; session.enablefilter("accesslevel").setparameter("userlevel", user.getaccesslevel()); |
下面的代码是一个正在装载的区域,它将过滤当前用户访问层的集合以得到一个子集。
| region region = (region) session.get(region.class, "emea"); region.getopportunities().size(); |
四、结论
本文简单介绍了hibernate3.x的使用方法,并给出了一些实例。虽然这些例子很简单,但却使我们对hibernate3.x过滤器的强大有了更深的认识。我们通过上面的例子也许会有更多好的想法,如果能将它们和不同的拦截方法,如web过滤器结合,将会发挥出更大的力量。
闽公网安备 35060202000074号