Guide oxmysql : MySQL.query.await, single, scalar, insert
Comment utiliser oxmysql dans FiveM Lua : MySQL.query.await, single, scalar, insert, update, prepare, paramètres sécurisés ?, l'import MySQL.lua, éviter les boucles et les transactions.
Vous avez besoin de lire ou d'écrire dans la base de données à partir d'un script serveur, et les exemples en ligne mélangent les anciens appels MySQL.Async, les callbacks et exports.oxmysql dans différents styles. Cet article montre les fonctions MySQL actuelles, laquelle utiliser pour quel résultat, et comment garder les requêtes sûres et bon marché.
Charger l'objet MySQL
Pour utiliser le global MySQL, importez le wrapper Lua d'oxmysql dans le manifeste de la ressource, avant vos propres scripts serveur :
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
server_scripts {
'@oxmysql/lib/MySQL.lua',
'server/main.lua',
}
dependency 'oxmysql'Sans cette ligne, MySQL est nil et vous obtenez attempt to index a nil value (global 'MySQL'). Le fichier fonctionne uniquement sur le serveur. Si oxmysql lui-même ne peut pas se connecter, consultez erreurs de connexion oxmysql.
Les fonctions et ce qu'elles retournent
Chaque fonction a une forme .await qui attend le résultat et le retourne. Elle doit s'exécuter dans un thread ou un gestionnaire d'événement, ce qui est déjà le cas pour la plupart de votre code.
| Fonction | Retourne | Utilisez-le pour |
|---|---|---|
MySQL.query.await |
Un tableau de lignes (vide si aucune) | SELECT avec plusieurs lignes |
MySQL.single.await |
Une ligne, ou nil |
SELECT pour un seul enregistrement |
MySQL.scalar.await |
Une valeur, ou nil |
COUNT, SUM, une colonne |
MySQL.insert.await |
Le nouvel ID auto-incrémenté | INSERT |
MySQL.update.await |
Nombre de lignes affectées | 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 })Vérifiez les résultats. Une requête single ou scalar qui ne trouve rien donne nil, et l'utiliser sans vérification est à la source de nombreuses erreurs attempt to index a nil value.
Paramètres avec ?
Toujours passer les valeurs via les espaces réservés ? et le deuxième argument, un tableau dans le même ordre :
-- safe
MySQL.query.await('SELECT * FROM users WHERE name = ?', { name })
-- dangerous: SQL injection
MySQL.query.await('SELECT * FROM users WHERE name = "' .. name .. '"')Avec la version non sécurisée, un nom tel que x" OR 1=1 -- réécrit votre requête. Avec les espaces réservés, oxmysql envoie les valeurs séparément, de sorte qu'elles ne peuvent jamais devenir du SQL. Cela s'applique à chaque valeur qui provient d'un joueur, d'un argument d'événement ou d'une commande.
Attention : les espaces réservés fonctionnent uniquement pour les valeurs, pas pour les noms de table ou de colonne. Si un nom de colonne provient de l'extérieur, vérifiez-le par rapport à une liste fixe de noms autorisés au lieu de le coller.
Style callback
Les mêmes fonctions acceptent un callback si vous ne voulez pas attendre. C'est utile en dehors d'un thread :
MySQL.query('SELECT * FROM users WHERE job = ?', { 'police' }, function(rows)
print(#rows)
end).await est plus lisible et c'est acceptable dans les événements. Utilisez le style callback quand vous voulez que le code qui suit continue de s'exécuter sans attendre la base de données.
MySQL.prepare
MySQL.prepare exécute une instruction préparée, que la base de données peut réutiliser quand vous exécutez la même requête encore et encore. Il utilise les mêmes paramètres ? :
local row = MySQL.prepare.await('SELECT * FROM users WHERE identifier = ?', { identifier })Ce qu'il retourne dépend du résultat : une seule ligne revient comme une table, et une requête avec une colonne et une ligne peut revenir comme une valeur. Pour le code où la forme du résultat a de l'importance, single et scalar sont plus prévisibles, donc utilisez prepare quand vous avez une requête chaude qui s'exécute souvent.
Ne pas faire de requête dans une boucle
Chaque requête est un aller-retour à la base de données. Une boucle qui exécute une requête par élément devient lentement lente, et elle bloque le thread pendant qu'elle attend :
-- one query per player
for _, id in ipairs(ids) do
local row = MySQL.single.await('SELECT * FROM users WHERE id = ?', { id })
endRécupérez tout dans une seule requête, puis bouclez sur le résultat en Lua :
local rows = MySQL.query.await('SELECT * FROM users WHERE job = ?', { 'police' })Pour de nombreuses insertions, créez une seule instruction avec plusieurs lignes, ou exécutez-les ensemble dans une transaction. Évitez également de faire une requête à chaque tick ou à chaque frame : lisez une valeur une seule fois et gardez-la dans une table Lua. Si une requête est lente, ajoutez un index sur la colonne par laquelle vous recherchez. Les requêtes lentes apparaissent comme avertissements de scintillement du thread serveur.
Transactions
Quand plusieurs écritures doivent tous réussir ou tous échouer, comme prendre de l'argent d'une ligne et le donner à une autre, utilisez une transaction :
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')
endSi une déclaration échoue, la base de données annule tout. Elle retourne true quand tout s'est bien passé.
Les tables doivent exister d'abord
Une requête contre une table qui n'existe pas donne Table 'x' doesn't exist. Créez la table avec un fichier SQL quand vous installez le script, ou au démarrage de la ressource avec MySQL.query.await('CREATE TABLE IF NOT EXISTS …'). Plus dans la table SQL n'existe pas.
Liste de contrôle
| Symptôme | Correction |
|---|---|
MySQL est nil |
Ajoutez '@oxmysql/lib/MySQL.lua' à server_scripts, avant vos fichiers |
| Le résultat est nil et le script génère une erreur | single et scalar retournent nil quand rien n'est trouvé : vérifiez-le |
| Les valeurs proviennent de joueurs | Utilisez ? et une table de paramètres, jamais de concaténation |
| Le serveur scintille sur une commande | Aucune requête dans une boucle : récupérez une fois, bouclez en Lua |
| Deux écritures doivent aller ensemble | MySQL.transaction.await |
| La même requête s'exécute constamment | MySQL.prepare, un index, ou mettez en cache la valeur en Lua |
Réponses rapides
Comment utiliser MySQL.query.await dans mon script ?
Ajoutez server_script '@oxmysql/lib/MySQL.lua' avant vos scripts serveur dans fxmanifest.lua, puis appelez MySQL.query.await('SELECT * FROM users WHERE id = ?', { id }) à l'intérieur d'un thread ou d'un gestionnaire d'événement.
Comment empêcher les injections SQL avec oxmysql ?
Ne construisez jamais la requête avec une concaténation de chaîne. Mettez des espaces réservés ? dans la requête et passez les valeurs dans une table, de sorte que oxmysql les envoie séparément.
Quelle est la différence entre query, single et scalar ?
query retourne toutes les lignes, single retourne la première ligne ou nil, et scalar retourne la première colonne de la première ligne, comme un décompte.
Des scripts sans ce problème
Shop CreatorCréez un magasin en moins d’une minute — propriétaires, employés, coffres et braquages inclus.Voir le script →
Item Creator V2Créez des items utilisables avec animations, props, effets et plus — sans écrire une ligne de code.Voir le script →
Crypto MiningAchetez un entrepôt, montez vos rigs pièce par pièce et minez sur un marché qui bouge.Voir le script →