在开发数据库应用的过程难免会编写大量的SQL语句,其中大部份是查询语句;为不同情况编写查询语句是一件很烦琐的事件。用过hibernate或Nhibernate会了解到把SQL查询语句对象化后使用起非常方便和快捷;也大大减少在编写查询SQL语句所带来的错误等问题。
前段时间在编写一个数据处理类的时候同样遇到这个问题,经过一段时间思考和设计现实现了SQL查询语句对象化的功能;在这里我把自己小小的成果共享一下。
在讲解前先看几个例子(数据是SQLServer的Northwind)注意:例子中所涉及的除查询对象化外还包含整个数据处理类的使用,那部分还在设计和完善当中。
1)以上语句是查询订单ID为10264的订单信息
using(HFSoft.Data.IDataSession session = HFSoft.Data.DataSessionFactory.OpenSession())
{
session.Open();
HFSoft.Data.QueryCmd query = new QueryCmd("Orders");
query.Expreesion.Add(new HFSoft.Data.EqExpression("OrderID",10264));
System.Data.DataSet myDS = session.ExecuteDataSet(query.BuilderCmd(session));
}
对象生成的SQL语句:
SELECT * FROM Orders where 1=1 And (OrderID = @OrderID0)
2)以上语句是查询订单ID大于10264并且小于10600的订单信息
using(HFSoft.Data.IDataSession session = HFSoft.Data.DataSessionFactory.OpenSession())
{
session.Open();
HFSoft.Data.QueryCmd query = new QueryCmd("Orders");
query.Expreesion.Add( new HFSoft.Data.LeExpression("OrderID",10264),
new HFSoft.Data.RtExpression("OrderID",10600));
System.Data.DataSet myDS = session.ExecuteDataSet(query.BuilderCmd(session));
}
对象生成的SQL语句:
SELECT * FROM Orders where 1=1 And (OrderID > @OrderID0) And (OrderID < @OrderID1)
4)以上语句是查询订单ID大于10264并且小于10600或编号是10601,10602,10605的订单信息
using(HFSoft.Data.IDataSession session = HFSoft.Data.DataSessionFactory.OpenSession())
{
session.Open();
HFSoft.Data.QueryCmd query = new QueryCmd("Orders");
query.Expreesion.Add( new HFSoft.Data.LeExpression("OrderID",10264),
new HFSoft.Data.RtExpression("OrderID",10600));
query.Expreesion.Add(HFSoft.Data.UintType.Or,new HFSoft.Data.InExpression("OrderID",new int[]{10601,10602,10605}));
System.Data.DataSet myDS = session.ExecuteDataSet(query.BuilderCmd(session)); }
对象生成的SQL语句:
SELECT * FROM Orders where 1=1 And (OrderID > @OrderID0) And (OrderID < @OrderID1) Or (OrderID in (@OrderID20,@OrderID21,@OrderID22))
从上面的例子我们可以看到对不同的条件进行数据查询只是一件很简单的事情,你并不用为不同的查询情况写相应SQL语句。
接下来讲术这个查询对象实现,对象的最终就是把不同字符串并起来生成相应的SQL语句;SQL语句查询语句主要分为以下几大部份:获取的字段,表名称,条件,排序,分组;了解SELECT语句的对查询语句的组成部分比较了解。
其中比较难的就是条件部分处理,因为条件的组合是情况是比较多;所以设计起来相对比较复杂。在设计的过程中把条件单独抽取出来,并生成接口对条件的表达式进行描述:
/// <summary>
/// 表达式描述接口
/// 用于SQL语句条件表达式的描述
/// </summary>
public interface IExpression
{
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>string</returns>
string GetFilter(HFSoft.Data.IDriverType driver);
/// <summary>
/// 获取表达式相关的参数
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver);
/// <summary>
/// 序列标识
/// 本属性用于内部处理
/// </summary>
string Sequence
{
get;
set;
}
/// <summary>
/// 添加表达式
/// </summary>
/// <param name="unittype">合并类型(or|and)</param>
/// <param name="expressions">表达式对象</param>
void Add(UintType unittype,params IExpression[] expressions );
/// <summary>
/// 添加表达式
/// </summary>
/// <param name="expressions">表达式对象</param>
void Add(params IExpression[] expressions );
}
在接口描述中有很多地方离不开HFSoft.Data.IDriverType它是用于描述数据库类型。根据HFSoft.Data.IDriverType 对应生成SqlServer,MySql,Oracle等数据库的条件表达式。
为什么IExpression具有Add方法,并且添加的对象也是IExpression;因为条件自己可以包含多个子表达式,只有这样才能够灵活组合成复杂的条件表达式。
接下来看下基于这个接口的实现
/// <summary>
/// 表达式基础类
/// </summary>
[Serializable]
public class Expression:IExpression
{
private string mName;
/// <summary>
/// 获取或设置相关的字段名
/// </summary>
public string Name
{
get
{
return mName;
}
set
{
mName = value;
}
}
private object mValue;
/// <summary>
/// 获取或设置相关的字段值
/// </summary>
public object Value
{
get
{
return mValue;
}
set
{
mValue = value;
}
}
private string mSequence = "";
/// <summary>
/// 获取或设置相关标识
/// 本属性用于内部处理
/// </summary>
public string Sequence
{
get
{
return mSequence;
}
set
{
mSequence = value;
}
}
#region IExpression 成员
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>string</returns>
public virtual string GetFilter(HFSoft.Data.IDriverType driver)
{
return " 1=1 " + GetSubString(driver);
}
/// <summary>
/// 获取表达式相关的参数
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
public virtual System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver)
{
return GetSubParams(driver);
}
#endregion
private System.Collections.ArrayList _Expressions = new System.Collections.ArrayList();
private System.Collections.ArrayList _Units = new System.Collections.ArrayList();
/// <summary>
/// 添加相关表达式
/// </summary>
/// <param name="expressions">表达式对象</param>
public void Add(params IExpression[] expressions )
{
Add(UintType.And,expressions);
}
/// <summary>
/// 添加相关表达式
/// </summary>
/// <param name="unittype">表达式合并类型</param>
/// <param name="expressions">表达式对象</param>
public void Add(UintType unittype,params IExpression[] expressions )
{
if(expressions != null)
foreach(IExpression exp in expressions)
{
if(exp != null)
{
_Units.Add(unittype.ToString());
exp.Sequence = this.Sequence +_Expressions.Count;
_Expressions.Add(exp);
}
}
}
/// <summary>
/// 获取内部表达式
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>string</returns>
protected string GetSubString(HFSoft.Data.IDriverType driver)
{
if(_Units.Count == 0)
return "";
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for(int i =0;i< this._Units.Count;i++)
{
sb.Append(" " +this._Units[i] +" ("+ ((IExpression)_Expressions[i]).GetFilter(driver)+")");
}
return sb.ToString();
}
/// <summary>
/// 获以内部表达式的参数值
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
protected System.Data.IDataParameter[] GetSubParams(HFSoft.Data.IDriverType driver)
{
if(_Expressions.Count ==0)
return null;
if(_Expressions.Count ==1)
{
return ((IExpression)_Expressions[0]).GetDataParams(driver);
}
System.Collections.ArrayList lst = new System.Collections.ArrayList();
foreach(IExpression exp in this._Expressions)
{
System.Data.IDataParameter[] ps = exp.GetDataParams(driver);
if(ps !=null && ps.Length >0)
{
foreach(System.Data.IDataParameter dp in ps)
{
lst.Add(dp);
}
}
}
Array array = Array.CreateInstance(typeof(System.Data.IDataParameter),lst.Count);
lst.CopyTo(array);
return array as System.Data.IDataParameter[];
}
}
其实Expression只是一个模板类,它自己本生并没有条件处理的能力只是一个简单的1=1;下面我们根据这个模板类派生出具体表达式类型。
/// <summary>
/// 基础表达式抽象类
/// </summary>
[Serializable]
public abstract class BaseExpression:Expression
{
/// <summary>
/// 获取表达式参数对象集
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
public override System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver)
{
System.Data.IDataParameter p1 = driver.GetParameter(Name+ Sequence,Value);
return ParamsConcat(new System.Data.IDataParameter[]{p1},GetSubParams(driver));
}
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>string</returns>
public override string GetFilter(HFSoft.Data.IDriverType driver)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(Name);
sb.Append(GetCompareType());
sb.Append(driver.FormatNameForParameter(Name + this.Sequence));
return sb.ToString();
}
/// <summary>
/// 表达式类型
/// =,like等
/// </summary>
/// <returns>string</returns>
protected abstract string GetCompareType();
}
/// <summary>
/// 等于表达式
/// </summary>
[Serializable]
public class EqExpression:BaseExpression
{
/// <summary>
/// 构造等于表达式对象
/// </summary>
public EqExpression()
{
}
/// <summary>
/// 构造指定名称和值的等于表达式对象
/// </summary>
/// <param name="name">名称</param>
/// <param name="value">值</param>
public EqExpression(string name,object value)
{
Name = name;
Value = value;
}
/// <summary>
/// 表达式比较符
/// </summary>
/// <returns>string</returns>
protected override string GetCompareType()
{
return " = ";
}
}
整个条件对象的设计就完成了,文章代码中只有实现了等于的表达式对象;我们可以按自己情况编写更复杂的表达式。条件表达式对象在整个查询对象中是比较核心的部心,因为在整个SQL查询语句中除了这些条件外其它地方都是固定的,剩下的就是把些不同的字符串合并起来,这些东西就不详细说了大家比较了解。
其实Expression只是一个模板类,它自己本生并没有条件处理的能力只是一个简单的1=1;下面我们根据这个模板类派生出具体表达式类型。
/// <summary>
/// 基础表达式抽象类
/// </summary>
[Serializable]
public abstract class BaseExpression:Expression
{
/// <summary>
/// 获取表达式参数对象集
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
public override System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver)
{
System.Data.IDataParameter p1 = driver.GetParameter(Name+ Sequence,Value);
return ParamsConcat(new System.Data.IDataParameter[]{p1},GetSubParams(driver));
}
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>string</returns>
public override string GetFilter(HFSoft.Data.IDriverType driver)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(Name);
sb.Append(GetCompareType());
sb.Append(driver.FormatNameForParameter(Name + this.Sequence));
return sb.ToString();
}
/// <summary>
/// 表达式类型
/// =,like等
/// </summary>
/// <returns>string</returns>
protected abstract string GetCompareType();
}
/// <summary>
/// 等于表达式
/// </summary>
[Serializable]
public class EqExpression:BaseExpression
{
/// <summary>
/// 构造等于表达式对象
/// </summary>
public EqExpression()
{
}
/// <summary>
/// 构造指定名称和值的等于表达式对象
/// </summary>
/// <param name="name">名称</param>
/// <param name="value">值</param>
public EqExpression(string name,object value)
{
Name = name;
Value = value;
}
/// <summary>
/// 表达式比较符
/// </summary>
/// <returns>string</returns>
protected override string GetCompareType()
{
return " = ";
}
}
整个条件对象的设计就完成了,文章代码中只有实现了等于的表达式对象;我们可以按自己情况编写更复杂的表达式。条件表达式对象在整个查询对象中是比较核心的部心,因为在整个SQL查询语句中除了这些条件外其它地方都是固定的,剩下的就是把些不同的字符串合并起来,这些东西就不详细说了大家比较了解。