[ MySQL ] DAAB MySql支持测试(Proc+DataSet更新) 5.0.27+.Net Connector 5.0.2beta

    以前装的MySQL 5.0.24a版本,因为有些问题,所以卸载掉重新装了5.0.27,感觉比5.0.24a那个版本要稳定,测试一下,在5.0.24a上存在的问题已经没有了。另外,以前用的ByteFX.Data,这次看到MySQL .Net Connector有了5.0.2版本,改用这个,毕竟官方正式的驱动要更可靠。
    参考: MySQL的使用 MySQL 5.0.24a + ByteFX.Data 0.76,里面提到的两个问题,在5.0.27下面不需要再处理。
    另外,使用MySQL 5.0.27 + MySQL .Net Connector 5.0.2 beta需要注意的地方,就是参数标志符号为?,而不是@。例如:select * from TblUser where UserID=?UID。在 MySQL的使用 MySQL 5.0.24a + ByteFX.Data 0.76中参数标志符号使用的就是@。
    因为想用MySQL写点东西,要在Enterprise Library的Data Access Application Block基础上加上MySQL支持,所以看了下MySQL以及MySql .Net Connector一些主要方面的用法。

    存储过程
    在test库里面建立测试表:
ContractedBlock.gif ExpandedBlockStart.gif 创建测试表TblUser
None.gifCREATE TABLE `test`.`TblUser` (
None.gif  `UserID` 
INTEGER UNSIGNED NOT NULL DEFAULT NULL AUTO_INCREMENT,
None.gif  `UserCode` 
VARCHAR(12NOT NULL,
None.gif  `UserName` 
VARCHAR(45),
None.gif  
PRIMARY KEY(`UserID`)
None.gif)
None.gifENGINE 
= InnoDB
None.gif
CHARACTER SET utf8 COLLATE utf8_general_ci;

    在MySQL Administrator的GUI工具里面,在User Administration里为当前操作的用户添加对test和系统库mysql的权限,让用户能创建存储过程。

    如果是在命令行或者是Query Browser创建存储过程,执行下面的语句:
ContractedBlock.gif ExpandedBlockStart.gif 创建存储过程SP_QueryUser
None.gifDELIMITER //
None.gif
CREATE PROCEDURE test.SP_QueryUser(
None.gif  
in UC varchar(12),
None.gif  
in UN varchar(45))
None.gif
BEGIN
None.gif     
select * from TblUser
None.gif     
where UserCode like UC and UserName like UN;
None.gif
END//
    如果是用MySQL Administrator创建存储过程,用下面的语句:
ContractedBlock.gif ExpandedBlockStart.gif 创建存储过程SP_QueryUser
None.gifCREATE PROCEDURE test.SP_QueryUser(
None.gif  
in UC varchar(12),
None.gif  
in UN varchar(45))
None.gif
BEGIN
None.gif     
select * from TblUser
None.gif     
where UserCode like UC and UserName like UN;
None.gif
END
    注意:
    1. DELIMITER //语句。MySQL的多个SQL语句/命令之间,默认使用;隔开,存储过程的body里面也要使用;将多个语句分隔开,这样,如果MySQL将创建存储过程的SQL语句使用;分隔,然后一个一个执行,就会有语法错误,无法创建存储过程。因此我们先使用DELIMITER关键字,将默认的分隔符修改为//(也可以是其它你认为合适的字符,例如?等),这样MySQL才会将存储过程的创建语句当作一个完整的SQL语句执行。
    2. 参数。参数名是不需要使用参数标志符号的,例如上面的例子,UC、UN两个参数名前面并没有加上参数标志符号?,如果加上参数标志符号会报语法错误。因为参数名不需要使用参数标志符号,因此注意参数名字别跟表的字段名字重名。
    3. 详细的语法,参考MySQL官方文档。一些摘选的SQL语法如下:
ContractedBlock.gif ExpandedBlockStart.gif MySQL语法摘选
None.gif#general statement#
None.gif
declare uid, id int;
None.gif
declare ucode varchar(12);
None.gif
declare uname varchar(45);
None.gif
set id=10;
None.gif
select UserID into uid from TblUser where UserCode='admin';
None.gif
None.gif#
if statement#
None.gif
if  then
None.gif  
None.gifelseif  
then
None.gif  
None.gif
end if;
None.gif
None.gif#
case statement#
None.gif
case 
None.gif  
when  then
None.gif    
None.gif  
when  then
None.gif    
None.gif  
else
None.gif    
None.gif
end case;
None.gif
None.gif#
while statement#
None.gif
while  do
None.gif  
None.gif
end while;
None.gif
None.gif#
cursor#
None.gif
declare rowCount int;
None.gif
declare my_cur cursor for select UserID, UserCode, UserName from TblUser order by UserCode;
None.gif
open my_cur;
None.gif  #you need control the loop 
count by yourself#
None.gif  
select count(*into rowCount from TblUser;
None.gif  
while rowCount>0 do
None.gif    
fetch my_cur into uid, ucode, uname;
None.gif    #finish your tasks#
None.gif    
None.gif    
set rowCount = rowCount - 1
None.gif  
end while;
None.gif
close my_cur;

    MySQL里面执行存储过程的语法为call StoredProcedureName ('参数1','参数2'),例如调用上面的存储过程为:
None.gif Call SP_QueryUser( ' % ' , ' % ' )
    用MySql.Data.MySqlClient调用存储过程,方式跟SQL Server一样:
ContractedBlock.gif ExpandedBlockStart.gif MySql.Data.MySqlClient调用存储过程
None.gifMySqlConnection connection = new MySqlConnection("Data source=localhost;User Id=root;Password=123;Database=test;");
None.gifconnection.Open();
None.gifMySqlCommand command 
= new MySqlCommand();
None.gifcommand.Connection 
= connection;
None.gifcommand.CommandType 
= CommandType.StoredProcedure;
None.gifcommand.CommandText 
= "SP_QueryUser";
None.gifcommand.Parameters.Add(
new MySqlParameter("?UC"this.TextBox1.Text.Trim() + "%"));
None.gifcommand.Parameters.Add(
new MySqlParameter("?UN"this.TextBox2.Text.Trim() + "%"));
None.gifMySqlDataAdapter adapter 
= new MySqlDataAdapter(command);
None.gifDataSet ds 
= new DataSet();
None.gifadapter.Fill(ds);
None.gifconnection.Close();
None.gif
None.gif
this.GridView1.DataSource = ds;
None.gif
this.GridView1.DataBind();

    在Enterprise Library Data Access Application Block里面,提供这样的方式调用存储过程:
None.gif public   virtual  DataSet ExecuteDataSet( string  storedProcedureName,  params   object [] parameterValues)
    你不必提供存储过程参数名列表,只需要提供object[]的参数值,在第一次调用存储过程时,DAAB会自动从数据库读取存储过程参数列表,并生成DbParameter对象缓存起来,后续调用这个存储过程就从缓存中读取参数列表,设置参数值,然后向数据库发送执行。象SQL Server的SqlDatabase类,通过调用SQL Server系统存储过程 sp_procedure_params_rowset(SQL 2005中通过调用sp_procedure_params_managed)实现。其实SQL Server的存储过程完全可以使用StoredProcedureName '参数1','参数2'的方式执行,但这种方式下需要将CommandType设成CommandType.Text,估计这样可能会导致数据库驱动并不是采用存储过程的方式执行命令,可能会造成存储过程的一些优化方面失效(直接把参数值拼到SQL里面,估计会以ad hoc方式处理;使用存储过程名带参数的方式,估计以RPC方式执行,但数据库驱动以及数据库服务器是以普通的RPC还是存储过程的方式对待不得而知)。当然,这个结论只是个人猜测,并没有去验证。
    不管怎样,按照MS的推荐方式使用是一种好的选择,因此在DAAB实现MySqlDatabase时也保持这样一种机制,这样就需要在MySQL中获取存储过程的参数列表。这点跟Oracle、Sql Server的ADO.Net驱动完全一样,使用MySqlCommandBuilder.DeriveParameters(MySqlCommand command)方法实现。
    下面的方法是我自己写的一段取存储过程参数名称列表的实现方法,只是一种尝试。MySqlCommandBuilder的DeriveParameters方法是通过调用MySQL接口完成的,并且会填充参数的Direction、Size、Scale等属性。
    MySQL里面存储过程信息保存在mysql数据库的proc表里面,param_list字段为参数列表:
None.gif SELECT  param_list  FROM  mysql. proc   where   proc .name = ' StoredProcedureName '   and   proc .db = ' DBName '
    param_list是一个BLOB类型,并且是存储过程创建语句的参数括号里面的原字符串,因此需要从这个字符串里面解析出参数名称。下面简单的用.Net代码示例解析参数名(不解析参数类型)。
ContractedBlock.gif ExpandedBlockStart.gif 取MySQL存储过程参数列表C#代码
None.gifusing System;
None.gif
using MySql.Data.MySqlClient;
None.gif
None.gif
public class MySqlUtil
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
public MySqlUtil()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//**//**//// <summary>
InBlock.gif    
/// 解析MySQL存储过程参数名列表
InBlock.gif    
/// </summary>
InBlock.gif    
/// <param name="procParam">mysql数据库proc表字段param_list的值</param>
InBlock.gif    
/// <param name="paramList">返回参数列表</param>
ExpandedSubBlockEnd.gif    
/// <returns>如果有参数,返回true,否则返回false</returns>

InBlock.gif    public static bool ProcParamList(object procParam, ref MySqlParameter[] paramList)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
bool result = false;
InBlock.gif
InBlock.gif        
if (!object.ReferenceEquals(procParam, null&& (procParam is byte[]))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
//将param_list值转换成字符串
InBlock.gif
            byte[] byteParam = (byte[])procParam;
InBlock.gif            global::System.Text.ASCIIEncoding encoding 
= new global::System.Text.ASCIIEncoding();
InBlock.gif            
string strParam = encoding.GetString(byteParam);
InBlock.gif            
//移除回车换行TAB等特殊控制字符
InBlock.gif
            strParam = strParam.Replace("\r""").Replace("\n""").Replace("\t""").Trim();
InBlock.gif            
//参数列表
InBlock.gif
            string[] arrayParams = strParam.Split(',');
InBlock.gif            
if (!object.ReferenceEquals(arrayParams, null&& arrayParams.Length > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
//用于过滤掉参数的输入、输出类型
InBlock.gif
                string tokens = "[IN][OUT][INOUT]";
InBlock.gif                global::System.Collections.ArrayList arrayList 
= new global::System.Collections.ArrayList(arrayParams.Length);
InBlock.gif                MySqlParameter mysqlParam;
InBlock.gif                
string[] oneParam;
InBlock.gif                
string param = "";
InBlock.gif                
for (int paramCount = 0; paramCount < arrayParams.Length; paramCount++)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
//param的格式类似于:IN/OUT/INOUT paramName Varchar(30)/INT等
InBlock.gif
                    param = arrayParams[paramCount];
InBlock.gif                    
if (!string.IsNullOrEmpty(param) && param.Trim().Length > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        oneParam 
= param.Split(' ');
InBlock.gif                        
for (int i = 0; i < param.Length; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            
//是否是空,或者是参数的Direction关键字(IN/OUT/INOUT)?
InBlock.gif
                            if (string.IsNullOrEmpty(oneParam[i]) || oneParam[i].Trim() == "" || tokens.IndexOf("[" + oneParam[i].Trim().ToUpper() + "]"> 0)
InBlock.gif                                
continue;
InBlock.gif                            
//得到参数名
InBlock.gif
                            mysqlParam = new MySqlParameter();
InBlock.gif                            mysqlParam.ParameterName 
= "?" + oneParam[i].Trim();
InBlock.gif                            arrayList.Add(mysqlParam);
InBlock.gif                            result 
= true;
InBlock.gif                            
break;
ExpandedSubBlockEnd.gif                        }

ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

InBlock.gif                paramList
=(MySqlParameter[])arrayList.ToArray(typeof(MySqlParameter));
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
return result;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif
None.gif
//调用上面方法的示例代码
None.gif
//取存储过程SP_QueryUser的参数
None.gif
MySqlConnection connection = new MySqlConnection("Data source=localhost;User Id=root;Password=123;Database=mysql;");
None.gifconnection.Open();
None.gifMySqlCommand command 
= new MySqlCommand("select param_list from mysql.proc where proc.name=?SPName and proc.DB=?DBName", connection);
None.gifcommand.Parameters.Add(
new MySqlParameter("?SPName""SP_QueryUser"));
None.gifcommand.Parameters.Add(
new MySqlParameter("?DBName""test"));
None.gifMySqlDataAdapter adapter 
= new MySqlDataAdapter(command);
None.gifDataSet ds 
= new DataSet();
None.gifadapter.Fill(ds);
None.gifconnection.Close();
None.gif
None.gif
//解析参数列表
None.gif
if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    MySqlParameter[] paramList 
= null;
InBlock.gif    
if (MySqlUtil.ProcParamList(ds.Tables[0].Rows[0]["param_list"], ref paramList))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
string s = "";
InBlock.gif        
foreach (MySqlParameter param in paramList)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            s 
= s + param.ParameterName + " ";
ExpandedSubBlockEnd.gif        }

InBlock.gif        
this.TextBox1.Text = s;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

    DataSet Update
    上面建的表TblUser中有一个自增类型的字段,为了简化DataSet Update的测试,建立了另外一个测试表TblItem:
ContractedBlock.gif ExpandedBlockStart.gif 创建测试表TblItem
None.gifCREATE TABLE `test`.`TblItem` (
None.gif  `ItemID` 
VARCHAR(36NOT NULL,
None.gif  `ItemNo` 
VARCHAR(20NOT NULL,
None.gif  `ItemName` 
VARCHAR(60),
None.gif  
PRIMARY KEY(`ItemID`)
None.gif)
None.gifENGINE 
= InnoDB
None.gif
CHARACTER SET utf8 COLLATE utf8_general_ci;

    DataSet Update的测试代码如下,整个代码跟Sql Server数据库写法上没有差别。测试过程中一开始在调用adapter.Update()方法后稍微停滞一段时间,然后出现一个"Server shutdown in progress"异常,把MySql的一些内存Buffer适当调大一些,重起MySql服务再进行测试就OK了。
ContractedBlock.gif ExpandedBlockStart.gif DataSet Update简单测试
None.gif//取DataSet
None.gif
MySqlConnection connection = new MySqlConnection("Data source=localhost;User Id=root;Password=123;Database=test;");
None.gifconnection.Open();
None.gif
string sql = "select * from tblitem";
None.gifMySqlCommand command 
= new MySqlCommand(sql, connection);
None.gifMySqlDataAdapter adapter 
= new MySqlDataAdapter(command);
None.gifDataSet ds 
= new DataSet();
None.gifadapter.Fill(ds);
None.gif
None.gif
//修改数据
None.gif
DataTable table = ds.Tables[0];
None.gif
//修改一行
None.gif
if (table.Rows.Count >= 1)
None.gif    table.Rows[
0]["ItemName"= table.Rows[0]["ItemName"].ToString() + "_o";
None.gif
//删除一行
None.gif
if (table.Rows.Count >= 2)
None.gif    table.Rows[
1].Delete();
None.gif
//插入两行
None.gif
DataRow row;
None.gifrow 
= table.NewRow();
None.gifrow[
"ItemID"= Guid.NewGuid().ToString().ToUpper();
None.gifrow[
"ItemNo"= "Item-" + DateTime.Now.AddDays(-2).ToString("yyMMddHHmmss");
None.gifrow[
"ItemName"= "Item-" + DateTime.Now.AddDays(-2).ToString("yyMMddHHmmss");
None.giftable.Rows.Add(row);
None.gifrow 
= table.NewRow();
None.gifrow[
"ItemID"= Guid.NewGuid().ToString().ToUpper();
None.gifrow[
"ItemNo"= "Item-" + DateTime.Now.AddDays(-1).ToString("yyMMddHHmmss");
None.gifrow[
"ItemName"= "Item-" + DateTime.Now.AddDays(-1).ToString("yyMMddHHmmss");
None.giftable.Rows.Add(row);
None.gif
None.gif
//更新数据
None.gif
//IsolationLevel.ReadUncommitted
None.gif
MySqlTransaction tran = connection.BeginTransaction();
None.gif
try
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    MySqlParameter param;
InBlock.gif    MySqlCommand update 
= new MySqlCommand("update test.tblitem set tblitem.ItemNo=?ItemNo, tblitem.ItemName=?ItemName where tblitem.ItemID=?ItemID", connection);
InBlock.gif    param 
= new MySqlParameter("?ItemNo", MySqlDbType.VarChar, 18, ParameterDirection.Input, false00"ItemNo", DataRowVersion.Current, null);
InBlock.gif    update.Parameters.Add(param);
InBlock.gif    param 
= new MySqlParameter("?ItemName", MySqlDbType.VarChar, 60, ParameterDirection.Input, true00"ItemName", DataRowVersion.Current, null);
InBlock.gif    update.Parameters.Add(param);
InBlock.gif    param 
= new MySqlParameter("?ItemID", MySqlDbType.VarChar, 36, ParameterDirection.Input, false00"ItemID", DataRowVersion.Original, null);
InBlock.gif    update.Parameters.Add(param);
InBlock.gif    update.Transaction 
= tran;
InBlock.gif
InBlock.gif    MySqlCommand insert 
= new MySqlCommand("insert into test.tblitem(ItemID, ItemNo, ItemName) values(?ItemID, ?ItemNo, ?ItemName)", connection);
InBlock.gif    param 
= new MySqlParameter("?ItemID", MySqlDbType.VarChar, 36, ParameterDirection.Input, false00"ItemID", DataRowVersion.Current, null);
InBlock.gif    insert.Parameters.Add(param);
InBlock.gif    param 
= new MySqlParameter("?ItemNo", MySqlDbType.VarChar, 18, ParameterDirection.Input, false00"ItemNo", DataRowVersion.Current, null);
InBlock.gif    insert.Parameters.Add(param);
InBlock.gif    param 
= new MySqlParameter("?ItemName", MySqlDbType.VarChar, 60, ParameterDirection.Input, true00"ItemName", DataRowVersion.Current, null);
InBlock.gif    insert.Parameters.Add(param);
InBlock.gif    insert.Transaction 
= tran;
InBlock.gif
InBlock.gif    MySqlCommand delete 
= new MySqlCommand("delete from test.tblitem where tblitem.ItemID=?ItemID", connection);
InBlock.gif    param 
= new MySqlParameter("?ItemID", MySqlDbType.VarChar, 36, ParameterDirection.Input, false00"ItemID", DataRowVersion.Original, null);
InBlock.gif    delete.Parameters.Add(param);
InBlock.gif    delete.Transaction 
= tran;
InBlock.gif
InBlock.gif    adapter.UpdateCommand 
= update;
InBlock.gif    adapter.InsertCommand 
= insert;
InBlock.gif    adapter.DeleteCommand 
= delete;
InBlock.gif
InBlock.gif    adapter.Update(table);
InBlock.gif    tran.Commit();
ExpandedBlockEnd.gif}

None.gif
catch
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    tran.Rollback();
ExpandedBlockEnd.gif}

None.gif
None.gif
//显示更新后的数据
None.gif
ds.Tables.Clear();
None.gifadapter.Fill(ds);
None.gifconnection.Close();
None.gif
None.gif
this.GridView1.DataSource = ds;
None.gif
this.GridView1.DataBind();

    有了上面这些了解,就可以从Enterprise Library Data Access Application Block的Database类继承,实现一个MySqlDatabase类了,并且非常简单。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值