Guia de consultas oxmysql: MySQL.query.await, single, scalar, insert

Como usar oxmysql em Lua do FiveM: MySQL.query.await, single, scalar, insert, update, prepare, parâmetros ? seguros, a importação MySQL.lua, evitar loops e transações.

Você precisa ler ou escrever no banco de dados de um server script, e exemplos online misturam chamadas antigas de MySQL.Async, callbacks e exports.oxmysql em estilos diferentes. Este artigo mostra as funções atuais de MySQL, qual usar para qual resultado, e como manter queries seguras e eficientes.

Carregue o objeto MySQL

Para usar o MySQL global, importe o wrapper Lua do oxmysql no manifesto do recurso, antes dos seus próprios server scripts:

lua
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

server_scripts {
    '@oxmysql/lib/MySQL.lua',
    'server/main.lua',
}

dependency 'oxmysql'

Sem essa linha, MySQL é nil e você recebe attempt to index a nil value (global 'MySQL'). O arquivo só funciona no servidor. Se o oxmysql em si não conseguir conectar, veja oxmysql connection errors.

As funções e o que elas retornam

Cada função tem uma forma .await que espera pelo resultado e o retorna. Ela deve rodar em uma thread ou um event handler, que é onde a maioria do seu código já roda.

Função Retorna Use para
MySQL.query.await Uma tabela de linhas (vazia se nenhuma) SELECT com muitas linhas
MySQL.single.await Uma linha, ou nil SELECT para um registro
MySQL.scalar.await Um valor, ou nil COUNT, SUM, uma coluna
MySQL.insert.await O novo id auto-incremento INSERT
MySQL.update.await Número de linhas afetadas UPDATE, DELETE
lua
-- 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 })

Verifique os resultados. Uma query single ou scalar que não encontra nada retorna nil, e usá-la sem uma verificação é a fonte de muitos erros attempt to index a nil value.

Parâmetros com ?

Sempre passe valores através de placeholders ? e do segundo argumento, uma tabela na mesma ordem:

lua
-- safe
MySQL.query.await('SELECT * FROM users WHERE name = ?', { name })

-- dangerous: SQL injection
MySQL.query.await('SELECT * FROM users WHERE name = "' .. name .. '"')

Na versão insegura, um nome como x" OR 1=1 -- reescreve sua query. Com placeholders, oxmysql envia os valores separadamente, então eles nunca podem virar SQL. Isso se aplica a todo valor que vem de um jogador, um argumento de evento ou um comando.

Atenção: placeholders só funcionam para valores, não para nomes de tabelas ou colunas. Se um nome de coluna vem de fora, verifique-o contra uma lista fixa de nomes permitidos em vez de colar diretamente.

Estilo callback

As mesmas funções aceitam um callback se você não quiser esperar. Isso é útil fora de uma thread:

lua
MySQL.query('SELECT * FROM users WHERE job = ?', { 'police' }, function(rows)
    print(#rows)
end)

.await é mais legível e funciona bem dentro de eventos. Use o estilo callback quando quiser que o código que segue continue rodando sem esperar pelo banco de dados.

MySQL.prepare

MySQL.prepare executa um prepared statement, que o banco de dados pode reutilizar quando você executa a mesma query repetidamente. Usa os mesmos parâmetros ?:

lua
local row = MySQL.prepare.await('SELECT * FROM users WHERE identifier = ?', { identifier })

O que ele retorna depende do resultado: uma única linha volta como uma tabela, e uma query com uma coluna e uma linha pode voltar como um valor. Para código onde a forma do resultado importa, single e scalar são mais previsíveis, então use prepare quando tiver uma query quente que roda frequentemente.

Não faça query em um loop

Cada query é uma ida e volta ao banco de dados. Um loop que roda uma query por item fica lento rapidamente, e bloqueia a thread enquanto espera:

lua
-- one query per player
for _, id in ipairs(ids) do
    local row = MySQL.single.await('SELECT * FROM users WHERE id = ?', { id })
end

Busque tudo em uma única query, depois faça um loop sobre o resultado em Lua:

lua
local rows = MySQL.query.await('SELECT * FROM users WHERE job = ?', { 'police' })

Para muitos inserts, construa uma única declaração com várias linhas, ou execute-as juntas em uma transação. Também evite fazer query a cada tick ou a cada frame: leia um valor uma vez e mantenha-o em uma tabela Lua. Se uma query é lenta, adicione um índice na coluna que você busca. Queries lentas aparecem como server thread hitch warnings.

Transações

Quando várias escritas devem todas ter sucesso ou todas falhar, como tirar dinheiro de uma linha e dar para outra, use uma transação:

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

Se qualquer declaração falhar, o banco de dados desfaz todas elas. Retorna true quando tudo passou.

Tabelas devem existir primeiro

Uma query contra uma tabela que não existe dá Table 'x' doesn't exist. Crie a tabela com um arquivo SQL quando você instalar o script, ou no resource start com MySQL.query.await('CREATE TABLE IF NOT EXISTS …'). Mais em SQL table doesn't exist.

Checklist

Sintoma Solução
MySQL é nil Adicione '@oxmysql/lib/MySQL.lua' a server_scripts, antes dos seus arquivos
Resultado é nil e o script gera erro single e scalar retornam nil quando nada é encontrado: verifique
Valores vêm de jogadores Use ? e uma tabela de parâmetros, nunca concatenação
Server trava em um comando Sem query em um loop: busque uma vez, faça loop em Lua
Duas escritas devem ir juntas MySQL.transaction.await
A mesma query roda constantemente MySQL.prepare, um índice, ou cache do valor em Lua

Respostas rápidas

Como uso MySQL.query.await no meu script?

Adicione server_script '@oxmysql/lib/MySQL.lua' antes dos seus server scripts em fxmanifest.lua, depois chame MySQL.query.await('SELECT * FROM users WHERE id = ?', { id }) dentro de uma thread ou event handler.

Como prevenho SQL injection com oxmysql?

Nunca construa a query com concatenação de strings. Coloque placeholders ? na query e passe os valores em uma tabela, assim oxmysql os envia separadamente.

Qual é a diferença entre query, single e scalar?

query retorna todas as linhas, single retorna a primeira linha ou nil, e scalar retorna a primeira coluna da primeira linha, como um count.

Scripts sem esse problema

Shop CreatorMonte uma loja em menos de um minuto — donos, funcionários, cofres e assaltos inclusos.Ver script →Item Creator V2Crie itens usáveis com animações, props, efeitos e muito mais — sem escrever código.Ver script →Crypto MiningCompre um galpão, monte rigs peça por peça e minere moedas num mercado que se mexe.Ver script →

Continue lendo