C#开发训练营

第29课:实现MySQL操作组件(2)

本课将完成支持MySQL数据库的tDb、tInsert、tUpdate和tDelete接口组件。

tDb接口

tDb接口作为构造器组件,可以方便创建各类数据组件对象,接口定义如下。

C#
public interface tDb
{
    string CnnStr { get; }
    tJet GetJet();
    tTable GetTable(string tableName);
    tInsert GetInsert(string tableName,string idName);
    tUpdate GetUpdate(string tableName);
    tDelete GetDelete(string tableName);
    tSelect GetSelect(string source);
    tBatch GetBatch(string tableName, string idName);
}

支持MySQL数据库tDb接口组件定义为tMySql类,实现如下(/app_code/data/mysql/tMySql.cs)。

C#
public class tMySql : tDb
{
    //
    public tMySql(string sCnnStr)
    {
        CnnStr = sCnnStr;
    }
    //
    public string CnnStr { get; private set; }
    //
    public tJet GetJet()
    { return new tMySqlJet(CnnStr); }
    ////
    public tTable GetTable(string tableName)
    { return new tMySqlTable(GetJet(), tableName); }
    ////
    public tInsert GetInsert(string tableName, string idName)
    { return new tMySqlInsert(GetJet(), tableName, idName); }
    ////
    public tUpdate GetUpdate(string tableName)
    { return new tMySqlUpdate(GetJet(), tableName); }
    ////
    public tDelete GetDelete(string tableName)
    { return new tMySqlDelete(GetJet(), tableName); }
    ////
    public tSelect GetSelect(string source)
    {
        return new tMySqlSelect(GetJet(), source);
    }
    ////
    public tBatch GetBatch(string tableName, string idName)
    { 
        return new tMySqlBatch(GetJet(), tableName, idName); 
    }
}

项目中的tApp类创建了Db字段(tDb接口类型),定义了项目中的主构造器对象,如下面的代码。

C#
using System;
using System.Web;

public class tApp
{
    public static string CnnStr1 = tMySqlHelper.GetCnnStr(
        "cdb_demo", "127.0.0.1", "root", "DEV_Test123456", 3306);
    public static tDb Db = new tMySql(CnnStr1);
    // 其它代码
}

代码中定义访问的数据库为MySQL,相关参数包括:

  • 数据库名称为cdb_demo。
  • 服务器地址为127.0.0.1,即本机。
  • 用户名为root,这是MySQL的默认管理员用户。
  • 登录密码为DEV_Test123456。
  • 端口使用默认的3306。

应用中,只需要使用tApp.Db对象的相关方法,就可以获取相应的数据库操作组件;比如,操作user_main表的操作类可以使用如下代码获取。

C#
tInsert ins = tApp.Db.GetInsert("user_main", "userid");
tUpdate upd = tApp.Db.GetUpdate("user_main");
tDelete del = tApp.Db.GetDelete("user_main");
tSelect sel = tApp.Db.GetSelect("user_main");
tDbBatch bat = tApp.Db.GetBatch("user_main","userid");

此外,获取tJet对象时,可以使用tApp.Db.GetJet()方法。目前,这些组件还没有实现,可以先让tMySql类中的方法返回null值,在创建组件后再修改为返回具体组件的对象。接下来将实现支持MySQL数据库操作的具体类型,首先从tInsert接口组件开始。

tInsert接口

tInsert接口用于向数据表的添加记录,接口定义如下。

C#
public interface tInsert
{
    tJet Jet { get; }
    string TableName { get; }
    string IdName { get; }
    // Insert方法返回新记录ID
    long Insert(tPairList data);
    long Insert(params tPair[] data);
    long Insert(string fields, string values);
    long Insert(params string[] nameValuePair);
}

在MySQL数据库中使用tMySqlInsert类实现tInsert接口,基础定义如下(/app_code/data/mysql/tMySqlInsert.cs)。

C#
using System;
using System.Text;
using MySql.Data.MySqlClient;

public class tMySqlInsert : tInsertBase
{
    public tMySqlInsert(tJet jet, string tableName, string idName)
    : base(jet, tableName, idName) { }
    // 其它代码...
}

tMySqlInsert类继承于tInsertBase类,构造函数需要指定MySQL数据库操作的Jet(tJet类型)、TableName和IdName属性数据;并通过继承基类的构造函数指定属性值。

下面的代码重写了Insert(tPairList)方法。

C#
public override long Insert(tPairList data)
{
    try
    {
        // 生成SQL
        StringBuilder sb = new StringBuilder("insert into ", 256);
        sb.AppendFormat("`{0}`(`{1}`", TableName, data[0].Name);
        StringBuilder sbValue = new StringBuilder(")values(?data_0",128);
        //
        for (int i = 1; i < data.Count; i++)
        {
            sb.AppendFormat(",`{0}`", data[i].Name);
            sbValue.AppendFormat(",?data_{0}", i);
        }
        sb.Append(sbValue.ToString());
        sb.Append(")");
        //
        using (MySqlConnection cnn = new MySqlConnection(Jet.CnnStr))
        {
            cnn.Open();
            MySqlCommand cmd = cnn.CreateCommand();
            using (MySqlTransaction tran = cnn.BeginTransaction())
            {
                cmd.CommandText = sb.ToString();
                for (int i = 0; i < data.Count; i++)
                    cmd.Parameters.AddWithValue("?data_" + i.ToString(), 
                        data[i].Value);
                IAsyncResult ar = cmd.BeginExecuteNonQuery();
                long result = -1000;
                if (cmd.EndExecuteNonQuery(ar) == 1)
                {
                    cmd.CommandText = "select @@identity";
                    cmd.Parameters.Clear();
                    result= tLng.GetValue(cmd.ExecuteScalar());
                    tran.Commit();
                }
                return result;
            }
        }
    }
    catch(Exception ex)
    {
        tLog.E(ex, -1000, "tMySqlInsert.Insert(tPairList)");
        return -1000;
    }
}

方法中首先会生成MySQL数据库使用的insert语句,其中,表名和字段名使用一对重音符号定义的对象格式,而values子句中的参数名使用了?data_0、?data_1、?data_2、……格式。方法生成的insert语句格式如下。

MySQL
insert into `<表>`(`<字段1>`,`<字段2>`,`<字段3>`,…) 
values(?data_0,?data_1,?data_2,…);

Insert(tPairList)方法中,使用事务(Transaction)完成新数据的添加工作,并通过select @@identity语句返回新记录的ID字段数据。如果方法返回大于0的整数,则表示数据添加成功。

Insert(tPair[])方法与Insert(tPairList)方法实现相似,如下面的代码。

C#
public override long Insert(params tPair[] data)
{
    try
    {
        // 生成SQL
        StringBuilder sb = new StringBuilder("insert into ", 256);
        sb.AppendFormat("`{0}`(`{1}`", TableName, data[0].Name);
        StringBuilder sbValue = new StringBuilder(")values(?data_0", 128);
        //
        for (int i = 1; i < data.Length; i++)
        {
            sb.AppendFormat(",`{0}`", data[i].Name);
            sbValue.AppendFormat(",?data_{0}", i);
        }
        sb.Append(sbValue.ToString());
        sb.Append(")");
        //
        using (MySqlConnection cnn = new MySqlConnection(Jet.CnnStr))
        {
            cnn.Open();
            MySqlCommand cmd = cnn.CreateCommand();
            using (MySqlTransaction tran = cnn.BeginTransaction())
            {
                cmd.CommandText = sb.ToString();
                for (int i = 0; i < data.Length; i++)
                    cmd.Parameters.AddWithValue("?data_" + i.ToString(),
                        data[i].Value);
                IAsyncResult ar = cmd.BeginExecuteNonQuery();
                long result = -1000;
                if (cmd.EndExecuteNonQuery(ar) == 1)
                {
                    cmd.CommandText = "select @@identity";
                    cmd.Parameters.Clear();
                    result = tLng.GetValue(cmd.ExecuteScalar());
                    tran.Commit();
                }
                return result;
            }
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tMySqlInsert.Insert(tPair[])");
        return -1000;
    }
}

下面的代码是Insert(string,string)方法的实现。

C#
public override long Insert(string fields, string values)
{
    try
    {
        StringBuilder sb = new StringBuilder(256);
        sb.AppendFormat("insert into `{0}`({1}) values({2});",
            TableName, fields, values);
        using (MySqlConnection cnn = new MySqlConnection(Jet.CnnStr))
        {
            cnn.Open();
            MySqlCommand cmd = cnn.CreateCommand();
            using (MySqlTransaction tran = cnn.BeginTransaction())
            {
                cmd.CommandText = sb.ToString();
                IAsyncResult ar = cmd.BeginExecuteNonQuery();
                long result = -1000;
                if (cmd.EndExecuteNonQuery(ar) == 1)
                {
                    cmd.CommandText = "select @@identity";
                    cmd.Parameters.Clear();
                    result = tLng.GetValue(cmd.ExecuteScalar());
                    tran.Commit();
                }
                return result;
            }
        }
    }
    catch(Exception ex)
    {
        tLog.E(ex, -1000, "tMySqlInsert.Insert(string,string)");
        return -1000;
    }
}

Insert(string,string)方法中,首先创建insert语句,格式如下。

MySQL
insert into `<表>`(<fields参数>) values(<values参数>);

创建insert语句后,同样通过事务执行,在执行insert语句后返回新记录的ID字段数据;当方法返回大于0的整数时表示数据添加成功。

最后是Insert(string[])方法的实现,如下面的代码。

C#
public override long Insert(params string[] nameValuePair)
{
    try
    {
        //
        if (nameValuePair.Length < 2 || nameValuePair.Length % 2 != 0)
            return -1001;
        //
        StringBuilder sb = new StringBuilder("insert into ", 256);
        sb.AppendFormat("`{0}`(`{1}`", TableName, nameValuePair[0]);
        StringBuilder sbValue = new StringBuilder(128);
        sbValue.AppendFormat(")values({0}", nameValuePair[1]);
        for (int i = 2; i < nameValuePair.Length; i += 2)
        {
            sb.AppendFormat(",`{0}`", nameValuePair[i]);
            sbValue.AppendFormat(",{0}", nameValuePair[i + 1]);
        }
        //
        sb.Append(sbValue.ToString());
        sb.Append(")");
        //
        using (MySqlConnection cnn = new MySqlConnection(Jet.CnnStr))
        {
            cnn.Open();
            MySqlCommand cmd = cnn.CreateCommand();
            using (MySqlTransaction tran = cnn.BeginTransaction())
            {
                cmd.CommandText = sb.ToString();
                IAsyncResult ar = cmd.BeginExecuteNonQuery();
                long result = -1000;
                if (cmd.EndExecuteNonQuery(ar) == 1)
                {
                    cmd.CommandText = "select @@identity";
                    cmd.Parameters.Clear();
                    result = tLng.GetValue(cmd.ExecuteScalar());
                    tran.Commit();
                }
                return result;
            }
        }
    }
    catch(Exception ex)
    {
        tLog.E(ex, -1000, "tMySqlInsert.Insert(string[])");
        return -1000;
    }
}

Insert(string[])方法中的参数每2个为一组,第一个为字段名,第二个为对应的数据;只有方法的参数大于2并且是偶数个时才会执行。和Insert(string,string)方法应用相似,直接使用字段和数据时要注意安全性和语句的通用性问题,特别是用户提交的数据,一般不应直接用于组合SQL语句,而是使用Insert(tPairLsit)或Insert(tPair[])方法,通过参数带入语句所需要的数据。此外,对于文本、二进制和日期时间数据注意使用一对单引号定义,而数值、null值等则直接使用字面量。

下面的代码,我们通过tInsert组件中的Insert(tPairList>方法向user_main表添加一条记录。

C#
tInsert ins = tApp.Db.GetInsert("user_main", "userid");
tPairList data = tPairList.Get(
    tPair.Get("username","user90"),
    tPair.Get("userpwd",tStr.Sha256("12345678")),
    tPair.Get("email","user90@aaa.bbb"),
    tPair.Get("locked",0));
long result = ins.Insert(data);
tWeb.WriteLine(result);

下面的代码可以完成同样的工作。

C#
tInsert ins = tApp.Db.GetInsert("user_main", "userid");
long result = ins.Insert(tPair.Get("username", "user91"),
    tPair.Get("userpwd", tStr.Sha256("12345678")),
    tPair.Get("email", "user90@aaa.bbb"),
    tPair.Get("locked", 0));
tWeb.WriteLine(result);

如果字段名和数据内容比较简单,也可以直接使用Insert(string,string)方法添加记录,如下面的代码。

C#
tInsert ins = tApp.Db.GetInsert("user_main", "userid");
string fields = "username,userpwd,email,locked";
string values = "'user92','"+tStr.Sha256("12345678")+"','user91@aaa.bbb',0";
long result = ins.Insert(fields,values);
tWeb.WriteLine(result);

下面使用Insert(string[])方法实现相同的功能。

C#
tInsert ins = tApp.Db.GetInsert("user_main", "userid");
string[] data = new string[]{
"username", "'user93'",
"userpwd","'"+tStr.Sha256("12345678")+"'",
"email","'user91@aaa.bbb'",
"locked","0"};
long result = ins.Insert(data);
tWeb.WriteLine(result);