QueryFilter是AppFramework提供的一个功能强大、不需要编写SQL语句、跨数据库、参数化、执行性能优异的查询条件构造器。有了QueryFilter我们不需要再拼装SQL语句,不用再担心单引号导致SQL注入的安全漏洞,不用担心被众多的AND、OR、NOT等逻辑运算符弄得晕头转向,不用区分处理TO_DATE、UPPER等无法跨数据库的表达式。因此,QueryFilter是AppFramework提供的一项非常重要的特色功能,极大地提升了数据库应用系统的开发效率。
QueryFilter
它是一种由多个项组成可嵌套的多层树状结构,通一层上的项与项之间是“与”或者“或”的逻辑关系,最顶层的项集合可以通过Items
属性获取到。QueryFilter
的结构非常直观简单,例如,页面上有三个查询条件,如下所示:

这三个条件就对应到三个QueryFilter
项,查询项之间的关系为“与”,代表着只有三个条件同时满足才能命中记录。QueryFilter
项可以由下列几种不同的类型组成:
1、 string
:SQL
表达式,确保可以实现任意功能,但无法保证可移植性;
2、 DBField
:字段表达式,具有较好的可移植性,可以描述“字段
操作符
值(或表达式)”这种形式的表达式,其中“值(或表达式)”可以有三种形式,分别是SQL
表达式、常量、SelectStatement
子查询;
3、 QueryFilter
:嵌套的QueryFilter
,例如:A and (B or C)
,其中的(B or C)
可以作为一个嵌套的QueryFilter
;
4、 SqlMap
:用于集成SQL
模板(SqlTemplate
)提供的功能。SQL
模板具有较强的可视性,容易编写和维护,在配置文件里编写后由代码生成器直接生成SqlMap
的子类,然后可以作为QueryFilter
的一个项来运行,这种做法可以提高SqlTemplate
的复用度,也提高了QueryFilter
的灵活性;
QueryFilter
属性方法说明:
属性/
方法
|
类型
|
说明
|
Items
|
ArrayList
|
用来存放组合条件的集合,其元素可以是 DBField
、QueryFilter
、String
、SqlMap
|
And
|
bool
|
指定各个组合条件之间的关系,
默认 And
|
Not
|
bool
|
指定是否对当前QueryFilter
进行取反运算,默认 false
|
QueryFilter(bool isAnd)
|
无
|
构造。
isAnd
为true
表示各个组合条件之间关系为And;
为false:
表示为Or
;默认为 true
|
Add(DBField field)
|
void
|
添加一个参数条件,
注意,field
的构造函数可以构造为子查询,因此此方法可以增加一项子查询
|
Add(QueryFilter filter)
|
void
|
添加一个子组合条件
|
AddSubFilter(bool isAnd)
|
QueryFilter
|
添加一个子组合条件
|
Add(string expression)
|
void
|
添加一个表达式
|
Add(string fieldName, DBOperator op, string queryString)
|
void
|
添加字段查询条件,若 queryString
为 null
或空则忽略
|
AddString(string fieldName, string queryString)
|
void
|
添加字符字段查询条件,不区分大小写,全模糊。若 queryString
为 null
或空则忽略。
|
AddString(string fieldName, string queryString, QueryFuzzyType fuzzyType)
|
void
|
添加字符字段查询条件,不区分大小写。若 queryString
为 null
或空则忽略。
|
AddString(string fieldName, string queryString, QueryFuzzyType fuzzyType, bool caseSensitive)
|
void
|
添加字符字段查询条件。若 queryString
为 null
或空则忽略
|
AddString(string fieldName, string queryString, QueryFuzzyType fuzzyType, bool caseSensitive)
|
void
|
添加字符字段查询条件。若 queryString
为 null
或空则忽略
|
AddDateRangeBegin(string fieldName, object date)
|
void
|
添加日期范围的开始日期。若 date
为 null
或空串则忽略
|
AddDateRangeEnd(string fieldName, object date)
|
void
|
添加日期范围的结束日期。若 date
为 null
或空串则忽略
|
Add(SqlMap sqlMap)
|
void
|
添加 SqlMap
|
在上一章节提到的例子中,可以这样构造QueryFilter:
QueryFilter filter = new QueryFilter(true); // true
表示项之间的逻辑关系为“
and
”
//
添加“名称”查询条件
NAME like "%xxx%"
filter.Items.Add(new DBField("NAME", DBOperator.Like, "'%" + txtName.Text + "%'");
//
添加“开始日期”查询条件
START_DATE >= xxx
filter.Add(new DBField("START_DATE", DBOperator.GreatThanOrEqual, DateTime.Parse(txtStartTime.Text).Date));
//
添加“结束日期”查询条件
END_DATE <= xxx
filter.Add(new DBField("END_DATE", DBOperator.LessThan, DateTime.Parse(txtEndTime.Text).Date.AddDays(1)));
using (IDBSession session = DBSessionManager.Default.GetSession())
{
DataTable dt = session. QueryDataTable("BAS_USER", "*", filter, null);
}
注意,添加“名称”查询条件也可以用直接添加SQL
表达式的方式,但要对输入项txtName.Text
进行单引号转义以避免产生SQL
注入漏洞,上面基于DBField
构造查询条件因生成参数化SQL
则没有漏洞问题。
//
添加“名称”查询条件,直接添加
SQL
表达式
filter.Add("name like '%"+txtName.Text.Replace("'", "''")+"%'");
但上面的写法还是过于简单,实际上对于字符串查询条件一般都要过滤掉头尾的空格,并且还要检验用户是否输入了,输入了才产生查询SQL
,因此完整的写法应该是:
QueryFilter filter = new QueryFilter(true); // true
表示项之间的逻辑关系为“
and
”
//
添加“名称”查询条件
NAME like "%xxx%"
string name = txtName.Text.Trim();
if (name.Length > 0)
{
filter.Items.Add(new DBField("NAME", DBOperator.Like, "'%" + name + "%'");
}
//
添加“开始日期”查询条件
START_DATE >= xxx
if (txtStartTime.Text.Length > 0)
{
filter.Add(new DBField("START_DATE", DBOperator.GreatThanOrEqual, DateTime.Parse(txtStartTime.Text).Date));
}
//
添加“结束日期”查询条件
END_DATE <= xxx
if (txtEndTime.Text.Length > 0)
{
filter.Add(new DBField("END_DATE", DBOperator.LessThan, DateTime.Parse(txtEndTime.Text).Date.AddDays(1)));
}
using (IDBSession session = DBSessionManager.Default.GetSession())
{
DataTable dt = session. QueryDataTable("BAS_USER", "*", filter, null);
}
为了简化查询业务的实现,QueryFilter
提供了几个非常智能的方法,能够快速实现输入预处理、非空判断、查询条件构造等,推荐使用下列方法实现查询业务:
QueryFilter
filter = new QueryFilter(true); // true
表示项之间的逻辑关系为“
and
”
//
添加“名称”查询条件
NAME like "%xxx%"
filter.Items.AddString("NAME", txtName.Text);
//
添加“开始日期”查询条件
START_DATE >= xxx
filter.AddDateRangeBegin("START_DATE", txtStartTime.Text);
//
添加“结束日期”查询条件
END_DATE <= xxx
filter. AddDateRangeEnd("END_DATE", txtEndTime.Text);
using (IDBSession session = DBSessionManager.Default.GetSession())
{
DataTable dt = session. QueryDataTable("BAS_USER", "*", filter, null);
}
注意,上述代码中许多常量被硬编码了,例如字段名“NAME
”、“BEGIN_DATE
”、表名“BAS_USER
”等,一种好的做法是采用代码生成器生成的元数据常量来编码,例如:
filter.AddString(BasUserDef.Name_FieldName, txtUserName.Text, QueryFuzzyType.RightFuzzy);
filter.AddDateRangeBegin(BasUserDef.UpdatedTime_FieldName, beginDate.Text);
filter.AddDateRangeEnd(BasUserDef.UpdatedTime_FieldName, endDate.Text);
此外,AddString
有多个重载方法,支持多种模糊查询选项,包括大小写是否区分,开发者可以根据实际需要选用其中一个方法:
/// <summary>
///
添加字符字段查询条件,不区分大小写,全模糊。若
queryString
为
null
或空则忽略。
/// </summary>
/// <param name="fieldName">
字段名
</param>
/// <param name="queryString">
查询值
</param>
public void AddString(string fieldName, string queryString);
/// <summary>
///
添加字符字段查询条件,不区分大小写。若
queryString
为
null
或空则忽略。
/// </summary>
/// <param name="fieldName">
字段名
</param>
/// <param name="queryString">
查询值
</param>
/// <param name="fuzzyType">
模糊方式
</param>
public void AddString(string fieldName, string queryString, QueryFuzzyType fuzzyType);
/// <summary>
///
添加字符字段查询条件。若
queryString
为
null
或空则忽略。
/// </summary>
/// <param name="fieldName">
字段名
</param>
/// <param name="queryString">
查询值
</param>
/// <param name="fuzzyType">
模糊方式
</param>
/// <param name="caseSensitive">
是否区分大小写
</param>
public void AddString(string fieldName, string queryString, QueryFuzzyType fuzzyType, bool caseSensitive);