#if MySQL using System; using System.Data; using MySql.Data.MySqlClient; using System.Collections.Generic; using System.Collections.Concurrent; using System.Threading.Tasks; using System.Text; using System.Diagnostics; using System.IO; using MrWu.Model; using System.Threading; using MrWu.Debug; using System.Data.SqlClient; namespace MrWu.DB { /// /// mysql操作 /// public class MySQL : SqlEx { /// /// 链接池 /// private class ConnectionPool : IConnectionPool { /// /// 链接字符串 /// private readonly string _connstr; string IConnectionPool.connecStr => _connstr; private readonly int _capsize; /// /// 最大的缓存链接数量 /// int IPool.capSize => _capsize; private readonly int _surviveCountSize; /// /// 同时存在最大的链接数量 /// /// int IPool.surviveCountSize => _surviveCountSize; /// /// 当前池中的数量 /// int IPool.capCount => _conns.Count; long _surviveCount = 0; /// /// 当前存活数量 /// int IPool.surviveCount { get { return (int)Interlocked.Read(ref _surviveCount); } } /// /// 所有的链接 /// private readonly ConcurrentQueue _conns = new ConcurrentQueue(); ConcurrentQueue IConnectionPool.conns => _conns; /// /// 用于异步等待连接可用的信号量 /// private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1); public ConnectionPool(SqlConfig config) { this._capsize = config.cacheCount; this._surviveCountSize = config.surviveCountSize; this._connstr = string.Format("server={0};port={1};user={2};password={3};AllowUserVariables={4};pooling=true;database=", config.host, config.port, config.username, config.pwd, config.AllowUserVariables); } /// /// 获取一个连接 /// /// /// public MySqlConnection TakeOut(params object[] dbbase) { if (dbbase.Length <= 0) return null; string dbname = dbbase[0] as string; if (dbname == null) return null; MySqlConnection conn; if (_conns.TryDequeue(out conn)) { if (conn != null) { return conn; } } if (this._surviveCountSize > 0 && Interlocked.Read(ref _surviveCount) >= this._surviveCountSize) return null; Interlocked.Increment(ref _surviveCount); return new MySqlConnection(this._connstr + dbname); } /// /// 异步获取一个连接(连接池满时不会阻塞线程) /// public async Task TakeOutAsync(string dbname) { if (dbname == null) return null; while (true) { MySqlConnection conn; if (_conns.TryDequeue(out conn)) { if (conn != null) return conn; } if (this._surviveCountSize == 0 || Interlocked.Read(ref _surviveCount) < this._surviveCountSize) { Interlocked.Increment(ref _surviveCount); return new MySqlConnection(this._connstr + dbname); } // 连接池满,异步等待 await _semaphore.WaitAsync(); _semaphore.Release(); } } /// /// 放入一个连接 /// /// public void PutInto(MySqlConnection conn) { if (_capsize == 0 || _conns.Count < _capsize) { _conns.Enqueue(conn); } else { conn.Dispose(); Interlocked.Decrement(ref _surviveCount); } } } /* * 1.执行sql语句 获得执行sql语句后的结果 * 2.执行存储过程 获得存储过程的结果 * * 每个库都有一个链接 * * */ private ConnectionPool _ConnPool = null; /// /// 链接池 /// public override IConnectionPool connPool => _ConnPool; /// /// 初始化 /// /// public MySQL(SqlConfig config) { this._ConnPool = new ConnectionPool(config); errorTryAgainCount = config.errorTryAgainCount; errorAgainTimeInc = config.errorAgainTimeInc; errorAgainTime = config.errorAgainTime; if (errorTryAgainCount < 0) throw new ArgumentException("errorTryAgainCount 不能小于 0"); if (errorAgainTimeInc < 0) throw new ArgumentException("errorAgainTimeInc 不能小于 0"); if (errorAgainTime < 0) throw new ArgumentException("errorAgainTime 不能小于 0"); } /// /// 获取一个连接 /// /// /// public override MySqlConnection GetConnection(string dbbase) { MySqlConnection tmp; while (true) { tmp = _ConnPool.TakeOut(dbbase); if (tmp != null) return tmp; Debug.Debug.Warning("链接数超过上限,等待重试"); Thread.Sleep(2); } } /// /// 异步获取一个连接(不会阻塞线程) /// public async Task GetConnectionAsync(string dbbase) { return await _ConnPool.TakeOutAsync(dbbase); } /// /// 开始存储过程 /// /// 存储过程所在的数据库 /// 存储过程名 /// public override IProdureParameter BeginProcedure(string dbbase, string procedure) { if (_ConnPool == null) throw new Exception("you dont hava init"); MySqlCommand cmd = new MySqlCommand(procedure, GetConnection(dbbase)); MySqlProcedureParameter result = new MySqlProcedureParameter(cmd, dbbase); //存储过程模式 cmd.CommandType = CommandType.StoredProcedure; return result; } /// /// 添加参数 /// /// 存储过程查询键 /// 参数名 /// 参数值 /// 参数类型 /// 参数种类 public override void AddParam(IProdureParameter spp, string paramName, object value, MySqlDbType type, ParameterDirection pd = ParameterDirection.Input) { if (spp == null || spp.command == null) throw new Exception("param spp null"); MySqlParameter param = spp.command.Parameters.Add(paramName, type); param.Direction = pd; if (value == null) param.Value = DBNull.Value; else param.Value = value; } /// /// 提交存储过程 /// /// 存储过程查询键 /// 如果 存储过程中有查询表操作,则需要传递DataSet实例 /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 /// 参数值,输出参数从字典中获取 public override Dictionary SubProcedure(IProdureParameter spp, DataSet ds = null, int againCount = -1) { if (spp == null || spp.command == null) throw new Exception("param spp null"); int cnt = Execute(spp.command, spp.dbName, ds, againCount); Dictionary dic = new Dictionary(); var pms = spp.command.Parameters; int len = pms.Count; for (int i = 0; i < len; i++) { dic.Add(pms[i].ParameterName, pms[i].Value); } if (!dic.ContainsKey(DbConst.RowsAffected)) { dic[DbConst.RowsAffected] = cnt; } spp.Dispose(); return dic; } /// /// 执行sql语句 /// /// 数据库 /// sql语句 /// /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 /// 如果是查询 使用传入ds实例 public override int ExecuteSql(string dbbase, string sql, IEnumerable mysqlparams = null, DataSet ds = null, int againCount = -1) { MySqlCommand cmd = new MySqlCommand(sql, GetConnection(dbbase)); cmd.CommandType = CommandType.Text; if (mysqlparams != null) { //int len = mysqlparams.Count; //for (int i = 0; i < len; i++) { // ISqlAloneParameter msap = mysqlparams[i]; // cmd.Parameters.Add(msap.paramname, msap.type, msap.valueLength); // cmd.Parameters[msap.paramname].Value = msap.paramvalue; //} foreach (var param in mysqlparams) { cmd.Parameters.Add(param); } } return Execute(cmd, dbbase, ds, againCount); } /// /// 执行sql语句 /// /// 数据库 /// sql语句 /// 附加参数 /// public MySqlCommand GetCommandBySql(string dbbase, string sql, IEnumerable mysqlparams = null) { MySqlCommand cmd = new MySqlCommand(sql, GetConnection(dbbase)); cmd.CommandType = CommandType.Text; if (mysqlparams != null) { //int len = mysqlparams.Count; //for (int i = 0; i < len; i++) { // ISqlAloneParameter msap = mysqlparams[i]; // cmd.Parameters.Add(msap.paramname, msap.type, msap.valueLength); // cmd.Parameters[msap.paramname].Value = msap.paramvalue; //} foreach (var param in mysqlparams) { cmd.Parameters.Add(param); } } return cmd; } /// /// 普通查询一个表 /// /// 数据库名称 /// 表名 /// 列名 /// 条件 /// 查询的数量 /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 /// sqlparams /// 查询到的表 public override DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null) { StringBuilder sb = new StringBuilder(); sb.Append("select "); if (column != null && column.Length > 0) { int len = column.Length; for (int i = len - 1; i >= 0; i--) { sb.Append(column[i]); if (i > 0) sb.Append(","); } } else { sb.Append("*"); } sb.Append(" from "); sb.Append(table); if (!string.IsNullOrEmpty(where)) { sb.Append(" where "); sb.Append(where); } if (count > 0) { sb.AppendFormat(" limit {0}", count); } DataSet ds = new DataSet(); ExecuteSql(dbbase, sb.ToString(), sqlparams, ds, againCount); if (ds.Tables == null || ds.Tables.Count == 0) return null; return ds.Tables[0]; } /// /// 执行sql命令 /// /// 指令 /// 数据库名称 /// null表示不需要查询结果集 有值将会把结果集放到 ds中 /// 重试次数 小于0表示使用配置值 大于等于0表示自定义 public override int Execute(MySqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) { // Debug.Debug.Log("执行sql指令!"); int result = 0; int exceCnt = 0; //执行次数 //重试次数 if (againCount < 0) againCount = errorTryAgainCount; try { while (exceCnt <= againCount) { try { if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open(); cmd.Connection.ChangeDatabase(dbname); if (ds == null) result = cmd.ExecuteNonQuery(); else { using(MySqlDataAdapter sda = new MySqlDataAdapter()) { sda.SelectCommand = cmd; sda.Fill(ds); } } break; } catch (Exception e) { cmd.Connection.Close(); if (exceCnt < againCount) Debug.Debug.Error("数据库执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message); else throw e; } int time = errorAgainTime + errorAgainTimeInc * exceCnt; exceCnt++; //执行次数+1 Thread.Sleep(time); } } finally { _ConnPool.PutInto(cmd.Connection); //放入链接 } return result; } /// /// 开始一个事务 /// /// 数据库 /// public override IProdureParameter BeginTransaction(string dbbase) { MySqlCommand cmd = new MySqlCommand(); MySqlProcedureParameter spp = new MySqlProcedureParameter(cmd, dbbase); try { cmd.Connection = GetConnection(dbbase); if (cmd.Connection.State == ConnectionState.Closed) cmd.Connection.Open(); cmd.Connection.ChangeDatabase(dbbase); cmd.Transaction = cmd.Connection.BeginTransaction(); //开始事务,还可以设置隔离等级 } catch (Exception e) { spp.exception = e; } return spp; } /// /// 事务添加sql语句 /// /// /// /// public override int TransactionAddSql(IProdureParameter spp, string sql, List> sqlparams = null) { if (spp.isException) return 0; try { MySqlCommand cmd = spp.command; cmd.CommandText = sql; cmd.Parameters.Clear(); if (sqlparams != null) { int len = sqlparams.Count; for (int i = 0; i < len; i++) { ISqlAloneParameter sap = sqlparams[i]; cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength); cmd.Parameters[sap.paramname].Value = sap.paramvalue; } } return cmd.ExecuteNonQuery(); } catch (Exception e) { spp.exception = e; throw e; } } /// /// 提交事务 /// /// /// true 表示提交成功 false 表示提交失败 public override bool SubTransaction(IProdureParameter spp) { try { spp.command.Transaction.Commit(); } catch (Exception e) { spp.exception = e; spp.command.Transaction.Rollback(); } finally { _ConnPool.PutInto(spp.command.Connection); spp.Dispose(); } return spp.exception == null; } /// /// 回滚事务 /// /// public override void RollbackTransaction(IProdureParameter spp) { try { spp.command.Transaction.Rollback(); } catch (Exception ex) { Debug.Debug.Error("RollbackTransaction ex: " + ex); } } #region 异步方法 /// /// 异步提交存储过程 /// public override async Task> SubProcedureAsync(IProdureParameter spp, DataSet ds = null, int againCount = -1) { if (spp == null || spp.command == null) throw new Exception("param spp null"); int cnt = await ExecuteAsync(spp.command, spp.dbName, ds, againCount); Dictionary dic = new Dictionary(); var pms = spp.command.Parameters; int len = pms.Count; for (int i = 0; i < len; i++) { dic.Add(pms[i].ParameterName, pms[i].Value); } if (!dic.ContainsKey(DbConst.RowsAffected)) { dic[DbConst.RowsAffected] = cnt; } spp.Dispose(); return dic; } /// /// 异步执行sql语句 /// public override async Task ExecuteSqlAsync(string dbbase, string sql, IEnumerable mysqlparams = null, DataSet ds = null, int againCount = -1) { MySqlCommand cmd = new MySqlCommand(sql, await GetConnectionAsync(dbbase)); cmd.CommandType = CommandType.Text; if (mysqlparams != null) { foreach (var param in mysqlparams) { cmd.Parameters.Add(param); } } return await ExecuteAsync(cmd, dbbase, ds, againCount); } /// /// 异步查询 /// public override async Task SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable sqlparams = null) { StringBuilder sb = new StringBuilder(); sb.Append("select "); if (column != null && column.Length > 0) { int len = column.Length; for (int i = len - 1; i >= 0; i--) { sb.Append(column[i]); if (i > 0) sb.Append(","); } } else { sb.Append("*"); } sb.Append(" from "); sb.Append(table); if (!string.IsNullOrEmpty(where)) { sb.Append(" where "); sb.Append(where); } if (count > 0) { sb.AppendFormat(" limit {0}", count); } DataSet ds = new DataSet(); await ExecuteSqlAsync(dbbase, sb.ToString(), sqlparams, ds, againCount); if (ds.Tables == null || ds.Tables.Count == 0) return null; return ds.Tables[0]; } /// /// 异步执行sql命令 /// public override async Task ExecuteAsync(MySqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) { int result = 0; int exceCnt = 0; if (againCount < 0) againCount = errorTryAgainCount; try { while (exceCnt <= againCount) { try { if (cmd.Connection.State == ConnectionState.Closed) await cmd.Connection.OpenAsync(); cmd.Connection.ChangeDatabase(dbname); if (ds == null) result = await cmd.ExecuteNonQueryAsync(); else { using(MySqlDataAdapter sda = new MySqlDataAdapter()) { sda.SelectCommand = cmd; sda.Fill(ds); } } break; } catch (Exception e) { cmd.Connection.Close(); if (exceCnt < againCount) Debug.Debug.Error("数据库异步执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message); else throw; } int time = errorAgainTime + errorAgainTimeInc * exceCnt; exceCnt++; await Task.Delay(time); } } finally { _ConnPool.PutInto(cmd.Connection); } return result; } #endregion /// /// 保存每个存储过程为单个文件 /// /// 那个数据库的 /// 存储的路径 /// 如果导出其中一个存储过程,则填存储过程名称 public void SaveMethod(string dbbase, string path, string method = null) { DataTable dt; if (method == null) dt = Select("information_schema", "routines", new string[] { "routine_name", "routine_type" }, string.Format("routine_schema='{0}'", dbbase)); else dt = Select("information_schema", "routines", new string[] { "routine_name", "routine_type" }, string.Format("routine_schema='{0}' and routine_name='{1}'", dbbase, method)); foreach (DataColumn item in dt.Columns) { Console.WriteLine(item.ColumnName); } int len = dt.Rows.Count; for (int i = 0; i < len; i++) { SaveMethod(dbbase, path, dt.Rows[i]["routine_name"] as string, dt.Rows[i]["routine_type"] as string); } } /// /// 保存方法 /// /// /// /// /// private void SaveMethod(string dbbase, string path, string methodName, string methodType) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string sql = string.Format("show create {0} {1};", methodType, methodName); DataSet ds = new DataSet(); ExecuteSql(dbbase, sql, null, ds); if (ds.Tables == null || ds.Tables.Count == 0 || ds.Tables[0].Rows == null || ds.Tables[0].Rows.Count == 0) return; path = string.Format("{0}/{1}.sql", path, methodName); string sqlCode = string.Format("Drop {0} if exists {1};{2}", methodType, methodName, Environment.NewLine); sqlCode += "Commit;" + Environment.NewLine; sqlCode += ds.Tables[0].Rows[0][string.Format("Create {0}", methodType)] as string; File.WriteAllText(path, sqlCode, Encoding.UTF8); } /// /// 执行sql文件 /// /// /// public void Execute(string dbbase, string path) { if (File.Exists(path)) { try { string sql = File.ReadAllText(path); ExecuteSql(dbbase, sql); } catch (Exception e) { Console.WriteLine("运行出错:" + path + Environment.NewLine + e.ToString()); } } else if (Directory.Exists(path)) { DirectoryInfo dinfo = new DirectoryInfo(path); string[] paths = Directory.GetDirectories(path); foreach (var item in paths) { Console.WriteLine(item); Execute(dbbase, item); } FileInfo[] finfos = dinfo.GetFiles(); foreach (var fl in finfos) { Execute(dbbase, fl.FullName); } } } } } #endif