Guía de consultas oxmysql: MySQL.query.await, single, scalar, insert

Cómo usar oxmysql en FiveM Lua: MySQL.query.await, single, scalar, insert, update, prepare, parámetros seguros ?, la importación MySQL.lua, evitar bucles y transacciones.

Necesitas leer o escribir en la base de datos desde un script de servidor, y los ejemplos en línea mezclan llamadas antiguas de MySQL.Async, callbacks y exports.oxmysql en diferentes estilos. Este artículo muestra las funciones MySQL actuales, cuál usar para cada resultado, y cómo mantener las consultas seguras y económicas.

Cargar el objeto MySQL

Para usar el MySQL global, importa el wrapper Lua de oxmysql en el manifiesto del recurso, antes de tus propios scripts de servidor:

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

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

dependency 'oxmysql'

Sin esa línea, MySQL es nil y obtendrás attempt to index a nil value (global 'MySQL'). El archivo solo funciona en el servidor. Si oxmysql en sí no puede conectarse, consulta errores de conexión de oxmysql.

Las funciones y lo que devuelven

Cada función tiene una forma .await que espera el resultado y lo devuelve. Debe ejecutarse en un thread o un manejador de eventos, que es donde ya se ejecuta la mayoría de tu código.

Función Devuelve Úsalo para
MySQL.query.await Una tabla de filas (vacía si no hay) SELECT con muchas filas
MySQL.single.await Una fila, o nil SELECT para un registro
MySQL.scalar.await Un valor, o nil COUNT, SUM, una columna
MySQL.insert.await El nuevo id de auto-incremento INSERT
MySQL.update.await Número de filas afectadas 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 })

Verifica los resultados. Una consulta single o scalar que no encuentra nada devuelve nil, y usarla sin una comprobación es la fuente de muchos errores attempt to index a nil value.

Parámetros con ?

Siempre pasa valores a través de placeholders ? y el segundo argumento, una tabla en el mismo orden:

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 versión insegura, un nombre como x" OR 1=1 -- reescribe tu consulta. Con placeholders, oxmysql envía los valores por separado, así que nunca pueden convertirse en SQL. Esto se aplica a cada valor que viene de un jugador, un argumento de evento o un comando.

Cuidado: los placeholders solo funcionan para valores, no para nombres de tablas o columnas. Si un nombre de columna viene de fuera, compruébalo contra una lista fija de nombres permitidos en lugar de pegarlo.

Estilo de callback

Las mismas funciones aceptan un callback si no quieres esperar. Esto es útil fuera de un thread:

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

.await se lee mejor y está bien dentro de eventos. Usa el estilo de callback cuando quieras que el código que sigue siga ejecutándose sin esperar la base de datos.

MySQL.prepare

MySQL.prepare ejecuta una sentencia preparada, que la base de datos puede reutilizar cuando ejecutas la misma consulta una y otra vez. Usa los mismos parámetros ?:

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

Lo que devuelve depende del resultado: una sola fila regresa como una tabla, y una consulta con una columna y una fila puede regresar como un valor. Para código donde la forma del resultado importa, single y scalar son más predecibles, así que recurre a prepare cuando tienes una consulta frecuente que se ejecuta a menudo.

No consultes en un bucle

Cada consulta es un viaje de ida y vuelta a la base de datos. Un bucle que ejecuta una consulta por elemento se ralentiza rápidamente, y bloquea el thread mientras espera:

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

Obtén todo en una única consulta, luego itera sobre el resultado en Lua:

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

Para muchos inserts, construye una sentencia con varias filas, o ejecútalos juntos en una transacción. También evita consultar en cada tick o cada frame: lee un valor una vez y mantenlo en una tabla Lua. Si una consulta es lenta, añade un índice en la columna que buscas. Las consultas lentas aparecen como advertencias de bloqueo de thread de servidor.

Transacciones

Cuando varios escritos deben tener éxito o fallar juntos, como sacar dinero de una fila y darlo a otra, usa una transacción:

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

Si alguna sentencia falla, la base de datos deshace todas ellas. Devuelve true cuando todo ha funcionado.

Las tablas deben existir primero

Una consulta contra una tabla que no existe da Table 'x' doesn't exist. Crea la tabla con un archivo SQL cuando instales el script, o al inicio del recurso con MySQL.query.await('CREATE TABLE IF NOT EXISTS …'). Más en la tabla SQL no existe.

Lista de verificación

Síntoma Solución
MySQL es nil Añade '@oxmysql/lib/MySQL.lua' a server_scripts, antes de tus archivos
El resultado es nil y el script da error single y scalar devuelven nil cuando no se encuentra nada: compruébalo
Los valores vienen de jugadores Usa ? y una tabla de parámetros, nunca concatenación
El servidor se bloquea en un comando Sin consultas en un bucle: obtén una vez, itera en Lua
Dos escritos deben ir juntos MySQL.transaction.await
La misma consulta se ejecuta constantemente MySQL.prepare, un índice, o cachea el valor en Lua

Respuestas rápidas

¿Cómo uso MySQL.query.await en mi script?

Añade server_script '@oxmysql/lib/MySQL.lua' antes de tus scripts de servidor en fxmanifest.lua, luego llama a MySQL.query.await('SELECT * FROM users WHERE id = ?', { id }) dentro de un thread o manejador de eventos.

¿Cómo evito inyección SQL con oxmysql?

Nunca construyas la consulta con concatenación de cadenas. Pon placeholders ? en la consulta y pasa los valores en una tabla, así oxmysql los envía por separado.

¿Cuál es la diferencia entre query, single y scalar?

query devuelve todas las filas, single devuelve la primera fila o nil, y scalar devuelve la primera columna de la primera fila, como un conteo.

Scripts que evitan este problema

Shop CreatorCrea una tienda en menos de un minuto — dueños, empleados, caja fuerte y atracos.Ver script →Item Creator V2Crea items usables con animaciones, props, efectos y más — sin escribir código.Ver script →Crypto MiningCompra una nave, monta rigs pieza a pieza y mina monedas en un mercado vivo.Ver script →

Sigue leyendo