第28课:实现MySQL操作组件(1)
接下来的几课将完成MySQL数据库操作组件的创建工作,这个过程中,可以对数据组件的实现进一步加深映像,为更多的扩展工作提供帮助;也可以对MySQL数据库操作进行回顾。本课实现了支持MySQL数据库的tJet和tTable接口组件。
tJet接口
tJet接口组件是数据库操作的基本组件,包括数据库连接字符串、连接测试、数据库相关特性和SQL执行并返回查询结果等操作;接口定义如下。
C# |
public interface tJet
{
string CnnStr { get; }
bool Connect();
//
string GetObjName(string name);
string GetParamName(string name);
char MatchChar { get; }
string Database { get; }
//
bool Execute(string sql, params tPair[] args);
int ExecuteNonQuery(string sql, params tPair[] args);
tPair GetValue(string sql, params tPair[] args);
tPairList GetFirstRow(string sql, params tPair[] args);
List<object> GetFirstColumn(string sql, params tPair[] args);
DataTable GetTable(string sql, params tPair[] args);
//
List<double> GetData(string sql, params tPair[] args);
}
|
操作MySQL数据库时,使用tMySqlJet类作为tJet接口中组件实现,下面的代码(/app_code/data/mysql/tMySqlJet.cs)是tMySqlJet类的基本实现,它继承于tJetBase类。
C# |
using System;
using System.Collections.Generic;
using System.Data;
using MySql.Data.MySqlClient;
public class tMySqlJet : tJetBase
{
public tMySqlJet(string sCnnStr) : base(sCnnStr)
{ }
//
public override bool Connect()
{
try
{
using(MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
return true;
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.Connect()");
return false;
}
}
//
public override string GetObjName(string name)
{
return "`"+name+"`";
}
//
public override string GetParamName(string name)
{
return "?" + name;
}
//
public override char MatchChar { get { return '%'; } }
//
public override string Database
{
get
{
MySqlConnectionStringBuilder sb =
new MySqlConnectionStringBuilder(CnnStr);
return sb.Database;
}
}
//
protected void AddParams(MySqlCommand cmd,params tPair[] args)
{
if(args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
cmd.Parameters.AddWithValue(GetParamName(args[i].Name),
args[i].Value);
}
}
// 其它代码...
}
|
代码中,首先通过继承tJetBase类的构造函数指定数据库连接字符串。接下来实现了tJet接口的一些成员和辅助成员,如:
- Connect()方法中,使用MySqlConnection对象连接MySQL数据库,正确连接数据时方法返回true,否则返回false。
- GetObjName()方法返回对象名格式,在MySQL数据库中,对象使用一对重音符号定义(键盘Tab键上方)。
- GetParamName()方法返回参数名,操作MySQL数据库时,参数名使用?定义,如?username。
- MatchChar属性定义了模糊查询字符串,这里同样使用%符号。
- Database属性返回连接的数据库名称,这里使用数据库连接字符串创建MySqlConnectionStringBuilder对象,并通过Database属性获取数据库名称。
- AddParams()方法,它的功能是将tPair数组指定的参数数据添加到MySqlCommand对象中。需要注意的是,方法中使用了GetParamName()方法将数据名转换为参数名。
下面的代码实现了tJet.Execute()方法,成功执行SQL时返回true,否则返回false。
C# |
public override bool Execute(string sql, params tPair[] args)
{
try
{
using(MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd,args);
IAsyncResult ar = cmd.BeginExecuteNonQuery();
cmd.EndExecuteNonQuery(ar);
return true;
}
}
catch(Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.Execute()");
return false;
}
}
|
下面的代码实现了tJet.ExecuteNonQuery()方法,执行SQL并返回影响的记录数量。
C# |
public override int ExecuteNonQuery(string sql, params tPair[] args)
{
try
{
using (MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd, args);
IAsyncResult ar = cmd.BeginExecuteNonQuery();
return cmd.EndExecuteNonQuery(ar);
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.ExecuteNonQuery()");
return -1000;
}
}
|
执行SQL并获取单个数据时使用GetValue()方法,tMySqlJet类中的实现如下。
C# |
public override tPair GetValue(string sql, params tPair[] args)
{
try
{
using (MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd, args);
return tPair.Get("", cmd.ExecuteScalar());
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.GetValue()");
return tPair.Get();
}
}
|
tJet.GetValue()会返回tPair对象,需要特定类型的数据时,可以使用对应的GetXXX()获取。
获取查询结果的第一行数据时使用GetFirstRow()方法,tMySqlJet类中的实现如下。
C# |
public override tPairList GetFirstRow(string sql, params tPair[] args)
{
try
{
using (MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd, args);
IAsyncResult ar = cmd.BeginExecuteReader();
using(MySqlDataReader dr = cmd.EndExecuteReader(ar))
{
tPairList lst =new tPairList();
if (dr.Read())
{
for (int col = 0; col < dr.FieldCount; col++)
lst.Add(dr.GetName(col), dr[col]);
}
return lst;
}
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.GetFirstRow()");
return tPairList.Get();
}
}
|
读取查询结果第一列的所有数据时使用GetFirstColumn()方法,实现如下。
C# |
public override List<object> GetFirstColumn(string sql, params tPair[] args)
{
try
{
using (MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd, args);
IAsyncResult ar = cmd.BeginExecuteReader();
using (MySqlDataReader dr = cmd.EndExecuteReader(ar))
{
List<object> lst = new List<object>();
while (dr.Read()) lst.Add(dr[0]);
return lst;
}
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.GetFirstRow()");
return new List<object>();
}
}
|
获取所有查询结果并返回DataTable对象时使用GetTable()方法,实现如下。
C# |
public override DataTable GetTable(string sql, params tPair[] args)
{
try
{
using (MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd, args);
using (MySqlDataAdapter ada = new MySqlDataAdapter(cmd))
{
DataSet ds = new DataSet();
ada.Fill(ds);
if (ds.Tables[0].Rows.Count > 0) return ds.Tables[0];
else return null;
}
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.GetTable()");
return null;
}
}
|
tJet.GetTable()方法中,如果查询结果没有记录会返回null,否则返回包含所有数据行的DataTable对象。
数据分析时,可以从数据库读取一列数据,并通过List<double>对象返回,此时使用GetData()方法,其实现如下。
C# |
public override List<double> GetData(string sql, params tPair[] args)
{
try
{
using (MySqlConnection cnn = new MySqlConnection(CnnStr))
{
cnn.Open();
MySqlCommand cmd = cnn.CreateCommand();
cmd.CommandText = sql;
AddParams(cmd, args);
IAsyncResult ar = cmd.BeginExecuteReader();
using (MySqlDataReader dr = cmd.EndExecuteReader(ar))
{
List<double> lst = new List<double>();
while (dr.Read()) lst.Add(dr.GetDouble(0));
return lst;
}
}
}
catch (Exception ex)
{
tLog.E(ex, -1000, "tMySqlJet.GetData()");
return new List<double>();
}
}
|
tTable接口
tTable接口组件用于数据表的基本操作,如创建、删除、重置、添加字段等,接口定义如下。
C# |
public interface tTable
{
tJet Jet { get; }
string Name { get; }
bool Exists();
string PrimaryKeys { get; set; }
List<tField> Fields { get; set; }
bool Create();
bool Drop();
bool Truncate();
bool AddFields(params tField[] fields);
}
|
在MySQL中进行数据表操作的tTable接口组件定义为tMySqlTable类,基本定义如下(/app_code/data/mysql/tMySqlTable.cs)。
C# |
using System;
using System.Text;
public class tMySqlTable : tTableBase
{
//
public tMySqlTable(tJet jet, string sName)
: base(jet, sName) { }
// 其它代码...
}
|
代码中,tMySqlTable定义为tTableBase类的子类,构造函数通过继承指定了Jet和Name属性。
Exists()方法用于判断表是否存在,实现如下。
C# |
public override bool Exists()
{
string sql = @"select TABLE_NAME from information_schema.TABLES
where TABLE_SCHEMA=?dbname and TABLE_NAME=?tblname";
return Jet.GetValue(sql,
tPair.Get("dbname", Jet.Database),
tPair.Get("tblname", Name)).GetStr() != "";
}
|
方法中,通过information_schema.TABLES表中的信息判断当前数据库中是否存在指定的数据表;information_schema.TABLES表中的TABLE_SCHEMA字段保存了数据库名称,TABLE_NAME字段保存了数据表名称。
接下来是两个辅助方法,首先是GetFieldSql()方法,其功能是将tField对象中的字段信息转换为字段定义语句;这里需要注意MySQL数据库中的字段类型定义,方法的实现代码如下。
C# |
protected string GetFieldSql(tField fld)
{
StringBuilder sb = new StringBuilder(Jet.GetObjName(fld.Name),32);
// 字段类型
if (fld.Type == tFieldType.Int)
sb.Append(" int");
else if (fld.Type == tFieldType.BigInt)
sb.Append(" bigint");
else if (fld.Type == tFieldType.Decimal)
sb.AppendFormat(" decimal({0},{1})", fld.Length,fld.DecimalPlace);
else if (fld.Type == tFieldType.VarChar)
sb.AppendFormat(" varchar({0})", fld.Length);
else if (fld.Type == tFieldType.Char)
sb.AppendFormat(" char({0})", fld.Length);
else if (fld.Type == tFieldType.DateTime)
sb.Append(" datetime");
else if (fld.Type == tFieldType.Binary)
sb.Append(" longblob");
else
sb.Append(" longtext");
// 是否为空
if (fld.Nullable == false)
sb.Append(" not null");
// 是否唯一约束
if (fld.Unique == true)
sb.Append(" unique");
// 默认值
if (fld.DefaultValue != null && fld.DefaultValue.Trim() != "")
{
if (fld.Type == tFieldType.Int || fld.Type == tFieldType.BigInt ||
fld.Type == tFieldType.Decimal)
sb.AppendFormat(" default {0}", fld.DefaultValue);
else
sb.AppendFormat(" default '{0}'", fld.DefaultValue);
}
//
return sb.ToString();
}
|
方法中,首先定义了字段名,这里使用了tJet接口组件的GetObjName()方法将字段名转换为数据库对象格式,如MySQL数据库就是使用一对重音符号(`)定义,而SQL Server数据库中使用方括号定义。
接下来是数据类型,这里使用MySQL数据库中对应的数据类型定义。
当Nullable属性定义为false时,说明字段数据不允许为空,此时,在字段定义中添加not null。
当Unique属性定义为true时,说明字段数据定义为唯一约束,此时,在字段定义中添加unique关键字。
当DefaultValue属性不为空对象(null)和空字符串时定义字段的默认值,此时,当数据类型为数值型直接使用属性内容,如果类型为文本、二进制、日期和时间类型,则使用一对单引号定义默认值内容。
最后,方法会返回字段定义的完整语句。
接下来的GetCreateSql()方法用于生成创建数据表的SQL语句,实现如下。
C# |
protected string GetCreateSql()
{
StringBuilder sb = new StringBuilder("create table",256);
sb.AppendFormat(" `{0}`(recid bigint not null auto_increment unique",Name);
// 字段定义
for (int i = 0; i < Fields.Count; i++)
{
sb.Append(",");
sb.Append(GetFieldSql(Fields[i]));
}
// 主键
if (PrimaryKeys != null)
{
string[] pk = PrimaryKeys.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
if (pk.Length > 0)
{
sb.AppendFormat(",primary key({0}",Jet.GetObjName(pk[0].Trim()));
for (int i = 1; i < pk.Length; i++)
sb.AppendFormat(",{0}",Jet.GetObjName(pk[i].Trim()));
sb.Append(")");
}
}
//
sb.Append(")engine=innodb default charset='utf8'");
return sb.ToString();
}
|
MySQL数据库中,ID字段一般使用bigint类型,并使用auto_increment关键字,此时,字段数据会从1开始,每次加1;这里,同时在ID字段中定义了唯一约束(unique)。当Fields属性不为空引用时,通过一个for循环结构将字段定义语句添加到表的创建语句中;在字段下定义后,还使用primary key子句定义了表的主键。最后,将数据表引擎定义为innodb,字符集定义为utf8。
Create()方法用于创建表,创建语句会通过tJet.Execute()方法执行,创建成功时方法返回true,否则返回false,实现代码如下。
C# |
public override bool Create()
{
string sql = GetCreateSql();
return Jet.Execute(sql);
}
|
需要向已存在的表添加字段时,可以使用AddFields()方法,实现代码如下。
C# |
public override bool AddFields(params tField[] fields)
{
if (fields.Length == 0) return false;
StringBuilder sb = new StringBuilder(256);
sb.AppendFormat("alter table `{0}` add column", Name);
sb.Append(GetFieldSql(fields[0]));
for (int i = 1; i < fields.Length; i++)
{
sb.Append(",add column ");
sb.Append(GetFieldSql(fields[i]));
}
return Jet.Execute(sb.ToString());
}
|
在MySQL数据库中,向表中添加字段使用alter table语句和add column子句;这里需要注意,添加多个字段时需要使用多个add column子句。
重置表和删除表的操作比较简单,分别使用truncate table和drop table语句,实现代码如下。
C# |
//
public override bool Drop()
{
return Jet.Execute("drop table `" + Name + "`");
}
//
public override bool Truncate()
{
return Jet.Execute("truncate table `" + Name + "`");
}
|