Guida alle query oxmysql: MySQL.query.await, single, scalar, insert

Come usare oxmysql in FiveM Lua: MySQL.query.await, single, scalar, insert, update, prepare, parametri safe ?, l'import MySQL.lua, evitare loop e transazioni.

Hai bisogno di leggere o scrivere il database da uno script server, e gli esempi online mescolano vecchie chiamate MySQL.Async, callback e exports.oxmysql in stili diversi. Questo articolo mostra le funzioni MySQL attuali, quale usare per quale risultato, e come mantenere le query sicure e efficienti.

Carica l'oggetto MySQL

Per usare il MySQL globale, importa il wrapper Lua di oxmysql nel manifest della risorsa, prima dei tuoi script server:

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

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

dependency 'oxmysql'

Senza quella riga, MySQL è nil e ottieni attempt to index a nil value (global 'MySQL'). Il file funziona solo sul server. Se oxmysql stesso non riesce a connettersi, vedi errori di connessione oxmysql.

Le funzioni e cosa ritornano

Ogni funzione ha una forma .await che aspetta il risultato e lo ritorna. Deve girare in un thread o un event handler, che è dove gira già la maggior parte del tuo codice.

Funzione Ritorna Usala per
MySQL.query.await Una tabella di righe (vuota se nessuna) SELECT con molte righe
MySQL.single.await Una riga, o nil SELECT per un record
MySQL.scalar.await Un valore, o nil COUNT, SUM, una colonna
MySQL.insert.await Il nuovo id auto-increment INSERT
MySQL.update.await Numero di righe interessate 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 })

Controlla i risultati. Una query single o scalar che non trova nulla dà nil, e usarla senza un controllo è la fonte di molti errori attempt to index a nil value.

Parametri con ?

Passa sempre i valori attraverso i placeholder ? e il secondo argomento, una tabella nello stesso ordine:

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

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

Con la versione non sicura, un nome come x" OR 1=1 -- riscrive la tua query. Con i placeholder, oxmysql invia i valori separatamente, quindi non possono mai diventare SQL. Questo vale per ogni valore che viene da un giocatore, da un argomento di evento o da un comando.

Attenzione: i placeholder funzionano solo per i valori, non per i nomi di tabelle o colonne. Se un nome di colonna viene da fuori, controllalo contro una lista fissa di nomi consentiti invece di incollarlo.

Stile con callback

Le stesse funzioni accettano un callback se non vuoi aspettare. Questo è utile fuori da un thread:

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

.await è più leggibile ed è fine dentro gli eventi. Usa lo stile callback quando vuoi che il codice che segue continui a girare senza aspettare il database.

MySQL.prepare

MySQL.prepare esegue un prepared statement, che il database può riusare quando esegui la stessa query più e più volte. Usa gli stessi parametri ?:

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

Cosa ritorna dipende dal risultato: una singola riga ritorna come una tabella, e una query con una colonna e una riga può ritornare come un valore. Per il codice dove la forma del risultato conta, single e scalar sono più prevedibili, quindi usa prepare quando hai una query hot che gira spesso.

Non fare query in un loop

Ogni query è un round trip al database. Un loop che esegue una query per elemento diventa lento velocemente, e blocca il thread mentre aspetta:

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

Prendi tutto in una singola query, poi fai un loop sul risultato in Lua:

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

Per molti insert, costruisci un'istruzione con più righe, o eseguile insieme in una transazione. Evita anche di fare query ogni tick o ogni frame: leggi un valore una volta e tienilo in una tabella Lua. Se una query è lenta, aggiungi un indice sulla colonna che cerchi. Le query lente compaiono come avvisi di server thread hitch.

Transazioni

Quando più scritte devono tutte riuscire o tutte fallire, come prendere soldi da una riga e darli a un'altra, usa una transazione:

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 qualche istruzione fallisce, il database annulla tutte loro. Ritorna true quando tutto è andato bene.

Le tabelle devono esistere prima

Una query contro una tabella che non esiste dà Table 'x' doesn't exist. Crea la tabella con un file SQL quando installi lo script, o all'avvio della risorsa con MySQL.query.await('CREATE TABLE IF NOT EXISTS …'). Di più in la tabella SQL non esiste.

Checklist

Sintomo Soluzione
MySQL è nil Aggiungi '@oxmysql/lib/MySQL.lua' a server_scripts, prima dei tuoi file
Il risultato è nil e lo script dà errore single e scalar ritornano nil quando non trovano nulla: controllalo
I valori vengono da giocatori Usa ? e una tabella di parametri, mai concatenazione
Server hitch su un comando Nessuna query in un loop: prendi una volta, fai loop in Lua
Due scritte devono andare insieme MySQL.transaction.await
La stessa query gira costantemente MySQL.prepare, un indice, o cachea il valore in Lua

Risposte rapide

Come posso usare MySQL.query.await nel mio script?

Aggiungi server_script '@oxmysql/lib/MySQL.lua' prima dei tuoi script server in fxmanifest.lua, poi chiama MySQL.query.await('SELECT * FROM users WHERE id = ?', { id }) dentro un thread o event handler.

Come posso prevenire SQL injection con oxmysql?

Non costruire mai la query con concatenazione di stringhe. Metti i placeholder ? nella query e passa i valori in una tabella, così oxmysql li invia separatamente.

Qual è la differenza tra query, single e scalar?

query ritorna tutte le righe, single ritorna la prima riga o nil, e scalar ritorna la prima colonna della prima riga, come un count.

Script senza questo problema

Shop CreatorCrea un negozio in meno di un minuto — proprietari, dipendenti, casseforti e rapine inclusi.Vedi script →Item Creator V2Crea oggetti utilizzabili con animazioni, props, effetti e altro — senza scrivere codice.Vedi script →Crypto MiningCompra un magazzino, costruisci rig pezzo per pezzo e mina monete su un mercato che si muove.Vedi script →

Continua a leggere