在早期做过报表系统开发的都知道,众多的查询条件和复杂的统计逻辑都直接硬编码在程序里,程序代码与SQL代码交织在一起,程序文件变得冗长杂乱,时间久了就变得难以维护。SqlTemplate是AppFramework提供的一种可视化、结构化的动态SQL语法,其代码为XML格式,从程序中剥离出来编写在.DaoGen文件里,很好地实现程序代码与SQL代码的分离,是一种全新的数据库编程模式。
AppFramework的SqlTemplate仿照IBatis的动态SQL语法,参数两端用“#”或“$”作标志。以“#”作标志的参数将生成参数化SQL,开发者不需要处理单引号转义;而以“$”作标志的参数将生成文本化的SQL,开发者要自己处理单引号转义,注意在编写“in ('a','b','c')”这种查询条件时,参数一定要用“$”作标志符,
例如:
USER_NO in ($IDs$)
而参数值则要赋值为:"'a','b','c'"。
在运行时,IDBSession会把SqlTemplate与传入的参数值进行绑定,最后得到完整的SQL语句并传给ADO.NET执行。目前参数值的载体仅支持IDictionary<string, object>对象,这一点与IBatis相比略有不足。
通过AppFramework.Tools.CodeGenPlugin代码生成器,可以把.DaoGen文件里配置好的模板生成对应的XxxSqlMap类,而XxxSqlMap类则派生于SqlMap。可以简单地用下图描述它们的关系:
生成的代码通常在XXX.Model命名空间下,例如:
/// <summary>
///
/// </summary>
public class GetBasUserListSqlMap : SqlMap
{
/// <summary>
///
获取内部
SQL
模板
/// </summary>
public static SqlTemplate GetBasUserListSqlTemplate = new SqlTemplate(@"<statement id=""GetBasUserList""><dynamic prepend="" "" append="""" seprator=""and""><isNotNull property=""UserName""> upper(NAME) like #UserName# </isNotNull><isNotNull property=""CreatedTimeBegin""> created_time >= #CreatedTimeBegin# </isNotNull></dynamic></statement>");
/// <summary>
///
获取
SQL
模板
/// </summary>
public override SqlTemplate Template
{
get
{
return GetBasUserListSqlTemplate;
}
}
/// <summary>
///
获取或设置
UserName
参数值
/// </summary>
public object UserName
{
set
{
if (value != null && value.ToString().Trim().Length > 0)
{
_parameters["UserName "] = value;
}
}
}
/// <summary>
///
获取或设置
CreatedTimeBegin
参数值
/// </summary>
public object CreatedTimeBegin
{
set
{
if (value != null && value.ToString().Trim().Length > 0)
{
_parameters["CreatedTimeBegin"] = value;
}
}
}
/// <summary>
///
获取绑定参数
/// </summary>
public override IDictionary<string, object> Parameters
{
get
{
return _parameters;
}
}
private IDictionary<string, object> _parameters = new Dictionary<string, object>(7);
}
SqlTemplate的XML代码编写在.DaoGen文件里,其语法与IBatis的动态Sql配置文件语法相似,但要简单实用得多。有关标签和属性说明如下:
标签
|
属性
|
说明
|
statement
|
|
此节点定义一个SqlTemplate,可以放在Map节点下,也可以放在Map/MapItem节点下。无论放在哪里,效果都是一样的。放在Map/MapItem节点下表示SqlTemplate与这个MapItem是相关的,仅起到归类的作用。
|
|
id
|
用于全局唯一标识statement。
|
dynamic
|
|
此节点定义一个动态代码块。
|
|
prepend
|
如果此块有非空动态项,则再块的头部插入代码。
|
|
append
|
如果此块有非空动态项,则再块的尾部插入代码。
|
|
seprator
|
非空动态项之间的间隔代码。例如:AND 或 OR
|
isNotNull
|
|
此节点定义一个动态项。
|
|
property
|
指定此动态项与哪个参数值绑定。如果参数集合里没有找到指定名称的参数,或参数值为null或空串,则不会输出此动态项所定义的SQL。
|
例子:
<statement id="GetBasUserList">
<dynamic prepend="" append="" seprator="and">
<isNotNull property="UserName"> upper(NAME) like #UserName# </isNotNull>
<isNotNull property="CreatedTimeBegin"> CREATED_TIME >= #CreatedTimeBegin# </isNotNull>
</dynamic>
</statement>
上面的模板中,(1)如果传入的参数是:
IDictionary<string,object> parameters = new Dictionary<string, object>();
parameters["UserName"] = "%abc%";
parameters["CreatedTimeBegin"] = "2006-10-1";
则生成SQL如下(以SQLServer为例):
upper(NAME) like @UserName and CREATED_TIME >= @CreatedTimeBegin
(2)如果传入的参数中没有
CreatedTimeBegin:
IDictionary<string,object> parameters = new Dictionary<string, object>();
parameters["UserName"] = "%abc%";
则生成SQL如下(以SQLServer为例):
upper(NAME) like @UserName
注意,上述SQL都是参数化的,参数值都已存放在SqlCommand.Parameters中。
1.1.3 在QueryFilter中使用SqlMap
SqlMap使用起来非常简单,只需实例化对象并给其属性赋值。在给属性赋值时类自动实现了对输入值的Trim()和非空判断。例如:
GetBasUserListSqlMap map = new GetBasUserListSqlMap();
map
.UserName = txtUserName.Text;
map
.CreatedTimeBegin = beginDate.Text;
using (IDBSession session = DBSessionManager.Default.GetSession())
{
DataTable dt = session.QueryDataTable(map.Template, map.Parameters);
return dt;
}