Приветствую!
Ранее я подавал заявку на конкурс статей, где хотел разобрать недостатки стандартного кубика для работы с БД (и получаемого из него кода) и вместе с вами пошагово разразработать в общем коде простейший (без каких-либо наворотов и заумностей) класс для работы с MySQL, позволяющий избежать этих недостатков. Но тогда не сложилось, вторую тему по MySQL не одобрили.
Сейчас конкурс шаблонов, поэтому теорию и пошаговую разработку мы пропустим, а я просто приложу шаблон-заготовку с простейшим классом в общем коде, который позволит избежать серьезных недостатков стандартного кода/кубика и сделать работу с MySQL более приятной.
Недостатки стандартного кубика/кода
Главный минус - нет возможности управлять открытием/закрытием коннектов/сессией. Каждый кубик/код работы с БД устанавливает соединение, открывает новую сессию, выполняет SQL запрос и закрывает сессию.
Отсюда проблемы:
- Невозможно выполнить множество запросов в рамках одной сессии с промежуточной обработкой данных. А именно это чаще всего и нужно для нормальной работы в многопотоке.
- Невозможно получить Id только что вставленной записи (повторный вызов кубика/кода откроет новую сессию где этой информации не будет)
- Открытие/закрытие подключений/сессий a) занимает доп. время б) создает доп. нагрузку. Если в шаблоне 50 кубиков работы с БД, то это 50 открытий/закрытий подключений/сессий на 1 поток. А если потоков сотни?
Вдобавок:
- Отсутствие функции экранирования спецсимволов (это обязательно нужно делать перед вставкой строковых данных в таблицу). Может я плохо искал?
- Насколько я помню код из кубика не хотел работать со списками созданными в C#, а работал только со списками созданными в ZP. Это очень неудобно.
- Код из кубика тяжел для восприятия - этакая сборная солянка из хоста/логина/пароля/бд/параметров подклобчения/sql запроса/прочих параметров
Итак, наша задача - решить описанные выше недостатки и сделать работу с БД MySQL более простой и наглядной.
Использование заготовки:
Подготовка ZP. Идем в директорию куда установлен ZP и в папке
Progs находим файл
MySql.Data.dll . Копируем его в папку
ExternalAssemblies находящуюся там же.
Далее открываем заготовку и в кубике C# пишем: (далее идут примеры):
Итак, пример(ы) кода:
string db_host = "localhost"; // хост
string db_user = "root"; // username для подключения к MySQL
string db_pswd = ""; // пароль для подключения к MySQL
string db_database = "mydb"; // название БД с которой будет работа
string db_charset = "utf8"; // кодировка данных в таблицах
// коннект к MySQL и открытие сессии
DB db = new DB(db_host, db_user, db_pswd, db_database, db_charset);
// все что идет ниже выполняется в рамках одного коннекта/сессии ... это очень важно (!)
// получить 1 результат (скаляр)
// для получения 1 результата всегда используем метод getOne
string count = db.getOne("SELECT COUNT(*) FROM accounts WHERE status=0");
project.SendInfoToLog("Кол-во аккаунтов: "+count,true); // выводим в лог ZP
// получить 1 запись ( запись = 1 строка разделенная на столбцы )
// для получения 1 записи/строки всегда используем метод getRow
List<string> row = db.getRow("SELECT first_name, last_name, status FROM accounts WHERE id=1");
if ( row.Count > 0 ){
project.SendInfoToLog("Имя: "+row[0],true); // выводим в лог ZP
project.SendInfoToLog("Фамилия: "+row[1],true); // выводим в лог ZP
project.SendInfoToLog("Статус: "+row[2],true); // выводим в лог ZP
}
else {
project.SendInfoToLog("запись отсутствует",true); // выводим в лог ZP
}
// для запросов не возвращающих результата (INSERT/UPDATE/LOCK/UNLOCK/...) всегда используем метод query
// лочим таблицу accounts что бы только 1 поток работал с ней
// P.S вы должны лочить все таблицы и их алиасы с которыми собираетесь работать в рамках строго 1 потока ..
// в этом примере работа идет лишь с 1 таблицей, поэтому и лочится только она
db.query("LOCK TABLES accounts WRITE");
// получить набор строк/столбцов
// для получения набора данных всегда используем метод getAll .. второй параметр - разделитель столбцов в строках ... его можно не указывать, по умолчанию он |
// берем 100 акков со status=0 (свободные), которые при этом дольше всех не брались ( ORDER BY check_time )
List<string> data = db.getAll("SELECT id, first_name, last_name FROM accounts WHERE status=0 ORDER BY check_time LIMIT 100","|");
List<string> ids = new List<string>(); // в этот список сохраним только id полученных данных
for(int i=0; i<data.Count; i++){
var x = data[i].Split('|');
project.SendInfoToLog("ID: "+x[0],true);
project.SendInfoToLog("Имя: "+x[1],true);
project.SendInfoToLog("Фамилия: "+x[2],true);
project.SendInfoToLog("--------------",true);
ids.Add(x[0]); // добавляем очередной id в список ids
}
// текущее юникс-время
int unixtime = (int)(DateTime.UtcNow - new DateTime(1970,1,1)).TotalSeconds;
// здесь, например, делаем чекинг id из списка ids на онлайн в ВК и наполнztv список online теми id которые сейчас онлайн
List<string> online = new List<string>();
// меняем статус у тех кто online (только их мы берем в работу)
if ( online.Count > 0 )
db.query("UPDATE accounts SET status=1 WHERE id IN("+string.Join(",",online)+")");
// обновляем время проверки у всех взятых id
if ( ids.Count > 0 )
db.query("UPDATE accounts SET check_time="+unixtime.ToString()+" WHERE id IN("+string.Join(",",ids)+")");
// разлочиваем таблицу
db.query("UNLOCK TABLES");
// пример экранирования спецсимволов в строке (если не экранировать, то одинарная кавычка поломает наш запрос)
string first_name = db.escapeString("Д'артаньян");
// вставка новой записи
db.query("INSERT INTO accounts SET first_name='"+first_name+"'");
// получение Id только что вставленной записи
string acc_id = db.getOne("SELECT LAST_INSERT_ID()");
project.SendInfoToLog("ID вставленной записи: "+acc_id,true);
// завершаем сессию
db.close();
Надеюсь данная заготовка кому-нибудь пригодится и поможет начать работать с MySQL т.к это открывает вам совершенно иные возможности при создании ваших шаблонов!
К посту приложены 2 файла - mysql_zagotovka.xmlz и primer1.xmlz . В первом файле не реализовывается какая-то конкретная логика, это просто заготовка со встроенным классом + кубик C# с примерами того как используя методы класса коннектиться к БД, делать различные запросы и тд (в общем то же самое что вы видите в примерах в этом посте).
Во втором файле (primer1.xmlz) реализована прямо конкретная логика по взятию аккаунтов в работу из БД в многопотоке, там достаточно создать свою таблицу нужной структуры (приведена мной несколькими постами ниже) и подставить свои данные для коннекта к БД. И все, можно юзать в своих проектах!