Руководство по запросам oxmysql: MySQL.query.await, single, scalar, insert
Как использовать oxmysql в FiveM Lua: MySQL.query.await, single, scalar, insert, update, prepare, безопасные ? параметры, импорт MySQL.lua, избегание циклов и транзакций.
Вам нужно прочитать или записать данные в базу данных из серверного скрипта, и примеры в интернете смешивают старые вызовы MySQL.Async, обратные вызовы и exports.oxmysql в разных стилях. Эта статья показывает текущие функции MySQL, какую использовать для какого результата и как сохранить запросы безопасными и быстрыми.
Загрузка объекта MySQL
Чтобы использовать глобальный MySQL, импортируйте оболочку Lua oxmysql в манифесте ресурса перед вашими серверными скриптами:
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
server_scripts {
'@oxmysql/lib/MySQL.lua',
'server/main.lua',
}
dependency 'oxmysql'Без этой строки MySQL будет nil и вы получите attempt to index a nil value (global 'MySQL'). Файл работает только на сервере. Если сам oxmysql не может подключиться, см. ошибки подключения oxmysql.
Функции и что они возвращают
Каждая функция имеет форму .await, которая ждет результат и возвращает его. Она должна работать в потоке или обработчике события, то есть там, где уже работает большинство вашего кода.
| Функция | Возвращает | Используйте для |
|---|---|---|
MySQL.query.await |
Таблица строк (пустая, если нет) | SELECT со множеством строк |
MySQL.single.await |
Одна строка или nil |
SELECT для одной записи |
MySQL.scalar.await |
Одно значение или nil |
COUNT, SUM, один столбец |
MySQL.insert.await |
Новый id автоинкремента | INSERT |
MySQL.update.await |
Количество затронутых строк | UPDATE, DELETE |
-- all rows
local rows = MySQL.query.await('SELECT identifier, name FROM users WHERE job = ?', { 'police' })
for _, row in ipairs(rows) do
print(row.identifier, row.name)
end
-- one row
local user = MySQL.single.await('SELECT * FROM users WHERE identifier = ?', { identifier })
if not user then return end
-- one value
local count = MySQL.scalar.await('SELECT COUNT(*) FROM owned_vehicles WHERE owner = ?', { identifier })
-- insert
local id = MySQL.insert.await('INSERT INTO my_logs (identifier, action) VALUES (?, ?)', { identifier, 'login' })
-- update or delete
local changed = MySQL.update.await('UPDATE users SET job = ? WHERE identifier = ?', { 'unemployed', identifier })Проверяйте результаты. Запрос single или scalar, который ничего не находит, возвращает nil, и использование без проверки — источник множества ошибок attempt to index a nil value.
Параметры с ?
Всегда передавайте значения через заполнители ? и второй аргумент, таблицу в том же порядке:
-- safe
MySQL.query.await('SELECT * FROM users WHERE name = ?', { name })
-- dangerous: SQL injection
MySQL.query.await('SELECT * FROM users WHERE name = "' .. name .. '"')С небезопасной версией имя типа x" OR 1=1 -- переписывает ваш запрос. С заполнителями oxmysql отправляет значения отдельно, поэтому они никогда не могут стать SQL. Это применяется к каждому значению, которое приходит от игрока, аргумента события или команды.
Внимание: заполнители работают только для значений, а не для имен таблиц или столбцов. Если имя столбца приходит откуда-то извне, проверьте его против фиксированного списка допустимых имен вместо вставки в запрос.
Стиль с обратным вызовом
Те же функции принимают обратный вызов, если вы не хотите ждать. Это полезно вне потока:
MySQL.query('SELECT * FROM users WHERE job = ?', { 'police' }, function(rows)
print(#rows)
end).await выглядит лучше и хорошо работает внутри событий. Используйте стиль обратного вызова, когда хотите, чтобы код, который следует дальше, продолжал выполняться без ожидания базы данных.
MySQL.prepare
MySQL.prepare запускает подготовленный оператор, который база данных может повторно использовать при запуске одного и того же запроса снова и снова. Он использует те же параметры ?:
local row = MySQL.prepare.await('SELECT * FROM users WHERE identifier = ?', { identifier })Что он возвращает, зависит от результата: одна строка возвращается как таблица, а запрос с одним столбцом и одной строкой может вернуть значение. Для кода, где форма результата имеет значение, single и scalar более предсказуемы, поэтому используйте prepare, когда у вас есть часто запускаемый запрос.
Не запрашивайте в цикле
Каждый запрос — это круговое путешествие в базу данных. Цикл, который запускает один запрос на элемент, становится медленным быстро и блокирует поток во время ожидания:
-- one query per player
for _, id in ipairs(ids) do
local row = MySQL.single.await('SELECT * FROM users WHERE id = ?', { id })
endЗагрузите все в один запрос, затем переберите результат в Lua:
local rows = MySQL.query.await('SELECT * FROM users WHERE job = ?', { 'police' })Для множества вставок постройте один оператор с несколькими строками или запустите их вместе в транзакции. Также избегайте запросов на каждом тике или каждом кадре: прочитайте значение один раз и сохраните его в таблице Lua. Если запрос медленный, добавьте индекс на столбец, по которому вы ищете. Медленные запросы появляются как предупреждения о задержке потока сервера.
Транзакции
Когда несколько записей должны либо все успешно выполниться, либо все не выполниться, например, при снятии денег с одной строки и отдаче их другой, используйте транзакцию:
local ok = MySQL.transaction.await({
{ query = 'UPDATE bank SET balance = balance - ? WHERE identifier = ?', values = { 100, fromId } },
{ query = 'UPDATE bank SET balance = balance + ? WHERE identifier = ?', values = { 100, toId } },
})
if not ok then
print('transfer failed, nothing was changed')
endЕсли любой оператор не выполнится, база данных отменит все. Она возвращает true, когда все прошло хорошо.
Таблицы должны существовать в первую очередь
Запрос к таблице, которой нет, дает Table 'x' doesn't exist. Создайте таблицу с SQL-файлом при установке скрипта или в начале ресурса с MySQL.query.await('CREATE TABLE IF NOT EXISTS …'). Подробнее в таблица SQL не существует.
Контрольный список
| Симптом | Исправление |
|---|---|
MySQL равен nil |
Добавьте '@oxmysql/lib/MySQL.lua' в server_scripts, перед вашими файлами |
| Результат равен nil и скрипт ошибается | single и scalar возвращают nil, когда ничего не найдено: проверьте |
| Значения приходят от игроков | Используйте ? и таблицу параметров, никогда конкатенацию |
| Сервер зависает на команде | Нет запроса в цикле: загрузите один раз, переберите в Lua |
| Две записи должны идти вместе | MySQL.transaction.await |
| Один и тот же запрос выполняется постоянно | MySQL.prepare, индекс или кешируйте значение в Lua |
Короткие ответы
Как использовать MySQL.query.await в своем скрипте?
Добавьте server_script '@oxmysql/lib/MySQL.lua' перед вашими серверными скриптами в fxmanifest.lua, затем вызовите MySQL.query.await('SELECT * FROM users WHERE id = ?', { id }) внутри потока или обработчика события.
Как предотвратить SQL-инъекции с oxmysql?
Никогда не строите запрос с конкатенацией строк. Поместите заполнители ? в запрос и передайте значения в таблице, чтобы oxmysql отправил их отдельно.
В чем разница между query, single и scalar?
query возвращает все строки, single возвращает первую строку или nil, а scalar возвращает первый столбец первой строки, например count.
Скрипты без этой проблемы
Shop CreatorМагазин меньше чем за минуту — владельцы, сотрудники, сейфы и ограбления в комплекте.Смотреть скрипт →
Item Creator V2Создавайте используемые предметы с анимациями, пропами, эффектами и не только — без кода.Смотреть скрипт →
Crypto MiningКупите склад, соберите фермы по деталям и майните монеты на живом рынке.Смотреть скрипт →