oxmysql FiveM คืออะไร ทำไม Script จำนวนมากถึงใช้

 oxmysql คือ Resource สำหรับเชื่อม FXServer ของ FiveM เข้ากับฐานข้อมูล MySQL หรือ MariaDB ทำให้ Server Scripts สามารถอ่าน เพิ่ม แก้ไข และลบข้อมูลใน Database ผ่าน API ที่ออกแบบมาสำหรับ FiveM เช่น query, single, scalar, insert, update, prepare และ transaction

โครงสร้างโดยรวมคือ

FiveM Client
↓
Server Resource
↓
oxmysql
↓
MySQL / MariaDB
↓
Database

ดังนั้นต้องเข้าใจก่อนว่า

oxmysql
≠ Database

ตัว Database จริงคือ

MySQL
หรือ
MariaDB

ส่วน oxmysql เป็นตัวกลางระหว่าง

FiveM Server Script
↕
Database Server

เหตุผลที่ Script FiveM จำนวนมากใช้ oxmysql คือมี API ครอบคลุม, รองรับ Lua/JavaScript, มีรูปแบบ .await, Parameterized Queries, Transactions, Prepared Queries, Slow Query Warnings และมี Compatibility สำหรับ Resource รุ่นเก่าที่เคยใช้ mysql-async หรือ ghmattimysql

① oxmysql คืออะไร

ชื่อ oxmysql มาจาก Database Resource ในกลุ่ม Overextended

หน้าที่หลักคือ

FXServer
↓
ส่ง SQL Query
↓
oxmysql
↓
MySQL/MariaDB
↓
รับ Result
↓
ส่งกลับ Server Script

ตัวอย่าง

local rows =
    MySQL.query.await(
        'SELECT * FROM users'
    )

Server Script ไม่ต้องจัดการ Network Protocol ของ MySQL เอง

oxmysql จัดการ Connection และ Database Communication ให้

② oxmysql ใช้ทำอะไรใน FiveM

ใช้กับระบบที่ต้องเก็บข้อมูลถาวร เช่น

Characters
Money
Bank
Inventory
Vehicles
Garages
Houses
Jobs
Businesses
Phone
Licenses
Punishments
Logs

ตัวอย่าง Garage ต้องหา Vehicle ของ Character

local vehicles =
    MySQL.query.await(
        [[
            SELECT *
            FROM vehicles
            WHERE character_id = ?
        ]],
        {
            characterId
        }
    )

ผลลัพธ์ถูกส่งกลับมาเป็น Lua Table

③ oxmysql เป็นฐานข้อมูลไหม

ไม่

นี่เป็นสิ่งที่มือใหม่ควรจำให้ชัด

MariaDB / MySQL
→ Database Server
oxmysql
→ Database Connector

ถ้าติดตั้ง oxmysql แต่ไม่มี Database Server ให้เชื่อมต่อ

ก็ไม่สามารถเก็บข้อมูลได้ตามปกติ

④ oxmysql เชื่อมกับ Database อย่างไร

Server กำหนด Connection String

ตัวอย่าง

set mysql_connection_string "mysql://username:password@localhost:3306/fivem"

จากนั้น Start oxmysql

ensure oxmysql

แล้ว Resources ที่ต้องใช้ Database จึง Start ตามมา

ensure my_core
ensure my_inventory
ensure my_garage

⑤ Connection String บอกอะไร

ตัวอย่าง

mysql://username:password@localhost:3306/fivem

ประกอบด้วย

username
password
host
port
database

Concept คือ

FXServer
↓
ใช้ Credentials
↓
เชื่อม Database Server
↓
เลือก Database fivem

รายละเอียดการตั้งค่าจะลงเต็มในบทความ 453

⑥ ทำไม oxmysql ต้อง Start ก่อน Script Database

สมมติ Garage ใช้

MySQL.query.await(...)

แต่ oxmysql ยังไม่พร้อม

Database API จะใช้ไม่ได้ตามที่ Resource คาดหวัง

ดังนั้น Startup Order ควรเป็น

Database Server
↓
oxmysql
↓
Framework/Core
↓
Gameplay Resources

ตัวอย่าง

ensure oxmysql
ensure my_core
ensure my_garage

⑦ Resource ควรประกาศ dependency ไหม

ถ้า Resource จำเป็นต้องใช้ oxmysql จริง สามารถระบุ

dependency 'oxmysql'

ใน fxmanifest.lua

ตัวอย่าง

fx_version 'cerulean'
game 'gta5'

dependency 'oxmysql'

server_script 'server.lua'

ทำให้ Requirement ของ Resource ชัดเจนขึ้น

⑧ วิธี Import oxmysql ใน Lua

Documentation ปัจจุบันรองรับ

server_script '@oxmysql/lib/MySQL.lua'

ใน fxmanifest.lua

และควรวาง Library ก่อน Server Scripts ที่เรียก MySQL

ตัวอย่าง

fx_version 'cerulean'
game 'gta5'

server_script '@oxmysql/lib/MySQL.lua'

server_scripts {
    'server/database.lua',
    'server/main.lua'
}

⑨ ทำไมต้องวาง MySQL.lua ก่อน Scripts อื่น

เพราะ Files ที่ตามมาจะสามารถใช้ Global API

MySQL

เช่น

MySQL.query.await(...)

ได้

ถ้า server/database.lua ถูกโหลดก่อน Library ที่ Resource Architecture พึ่งอยู่ ก็อาจเกิดปัญหาตอนเรียก API

⑩ oxmysql ใช้ Export ตรง ๆ ได้ไหม

มี APIs ผ่าน Exports โดยเฉพาะ JavaScript และ Compatibility Cases

แต่สำหรับ Lua Documentation แนะนำให้ Import

@oxmysql/lib/MySQL.lua

เพราะให้ Syntax อย่าง

MySQL.query.await(...)

ที่อ่านง่ายและมีประโยชน์ด้าน Debug/Type Handling เพิ่มเติมตาม Library

⑪ API หลักของ oxmysql มีอะไร

กลุ่มที่ Developer FiveM ควรรู้คือ

MySQL.query
MySQL.single
MySQL.scalar
MySQL.insert
MySQL.update
MySQL.prepare
MySQL.transaction

แต่ละตัวเหมาะกับ Result Type คนละแบบ

ไม่จำเป็นต้องใช้ query กับทุกกรณี

⑫ MySQL.query คืออะไร

ใช้เมื่อ Query อาจคืนหลาย Rows

ตัวอย่าง

local vehicles =
    MySQL.query.await(
        [[
            SELECT id, plate, model
            FROM vehicles
            WHERE character_id = ?
        ]],
        {
            characterId
        }
    )

ผลลัพธ์ประมาณ

{
    {
        id = 10,
        plate = 'ABC123',
        model = 'sultan'
    },
    {
        id = 11,
        plate = 'XYZ456',
        model = 'blista'
    }
}

⑬ query เหมาะกับอะไร

เช่น

รายชื่อรถทั้งหมด
รายการสินค้า
Transaction History
Player Logs
Inventory Rows
House List

หากคาดว่าจะได้หลาย Records

query เหมาะกว่า single

⑭ MySQL.single คืออะไร

ใช้เมื่อคาดว่าจะต้องการเพียง Row เดียว

ตัวอย่าง

local player =
    MySQL.single.await(
        [[
            SELECT id, cash, bank
            FROM characters
            WHERE id = ?
            LIMIT 1
        ]],
        {
            characterId
        }
    )

แล้ว

if not player then
    return
end

print(
    player.cash
)

⑮ single ต่างจาก query อย่างไร

query

คืนหลาย Rows
→ Array/Table of rows

single

คืน Row เดียว
→ Row table

หากต้องการ User คนเดียว single ทำ Intent ของ Code ชัดกว่า

⑯ MySQL.scalar คืออะไร

ใช้เมื่อเราต้องการเพียง Column แรกของ Row เดียว

เช่นหาเงิน

local cash =
    MySQL.scalar.await(
        [[
            SELECT cash
            FROM characters
            WHERE id = ?
            LIMIT 1
        ]],
        {
            characterId
        }
    )

ผลคือ

5000

ไม่ใช่ Row Table ทั้งชุด

⑰ scalar เหมาะกับอะไร

เช่น

COUNT(*)
cash
bank
name
boolean-like state
single aggregate value

ตัวอย่าง

local count =
    MySQL.scalar.await(
        [[
            SELECT COUNT(*)
            FROM vehicles
            WHERE character_id = ?
        ]],
        {
            characterId
        }
    )

⑱ MySQL.insert คืออะไร

ใช้กับ INSERT

และคืน Insert ID ในกรณีที่ Database Query มี Auto Increment ID

ตัวอย่าง

local vehicleId =
    MySQL.insert.await(
        [[
            INSERT INTO vehicles (
                character_id,
                plate,
                model
            )
            VALUES (?, ?, ?)
        ]],
        {
            characterId,
            plate,
            model
        }
    )

จากนั้น

print(
    'New vehicle ID:',
    vehicleId
)

⑲ insert เหมาะกับอะไร

เช่นสร้าง

Character
Vehicle
Property
Invoice
Business
Log record

ที่ต้องการ Row ID ใหม่กลับมา

⑳ MySQL.update คืออะไร

ใช้กับ Query ที่แก้ข้อมูล และคืนจำนวน Rows ที่ได้รับผล

ตัวอย่าง

local affectedRows =
    MySQL.update.await(
        [[
            UPDATE vehicles
            SET stored = ?
            WHERE id = ?
        ]],
        {
            1,
            vehicleId
        }
    )

สามารถตรวจ

if affectedRows < 1 then
    return
end

เพื่อดูว่ามี Record ถูกเปลี่ยนจริงหรือไม่

㉑ update ใช้กับ DELETE ได้ไหม

API ชื่อ update เน้น Result แบบจำนวน Rows ที่ได้รับผล

Query ประเภท Update/Delete บางกรณีสามารถใช้ API ที่คืน Affected Rows ตามรูปแบบที่เหมาะสม

แต่ Code ควรเลือก Method ให้ Intent อ่านเข้าใจง่าย

㉒ MySQL.prepare คืออะไร

prepare ใช้กับ Prepared Query Pattern

ตัวอย่าง

local player =
    MySQL.prepare.await(
        [[
            SELECT id, cash
            FROM characters
            WHERE id = ?
        ]],
        {
            characterId
        }
    )

Return Type จะขึ้นกับชนิด SQL Query

Prepared Queries เหมาะโดยเฉพาะกับ Query ที่ใช้ซ้ำบ่อย ๆ

㉓ prepare ทำให้ทุก Query เร็วขึ้นเสมอไหม

ไม่ควรคิดแบบนั้น

Performance ขึ้นกับ

Query Design
Indexes
จำนวน Rows
Database Load
Network
Execution Frequency

Prepared Query เป็นหนึ่งในเครื่องมือ

ไม่ใช่ปุ่ม

เปิดแล้ว Database เร็วทันที

㉔ MySQL.transaction คืออะไร

ใช้จัดหลาย Queries ให้เป็น Transaction เดียว

Concept

Query A
+
Query B
+
Query C

ทุกตัวสำเร็จ
→ Commit

มีตัวใดล้มเหลว
→ Rollback

เหมาะกับข้อมูลที่ต้อง Consistent กัน

㉕ ตัวอย่าง Transaction โอนเงิน

ต้องการ

Account A
- 1000

Account B
+ 1000

ไม่ควรหัก A สำเร็จแต่เพิ่ม B ล้มเหลว

ใช้ Transaction

local success =
    MySQL.transaction.await({
        {
            [[
                UPDATE accounts
                SET balance =
                    balance - ?
                WHERE id = ?
            ]],
            {
                amount,
                senderId
            }
        },
        {
            [[
                UPDATE accounts
                SET balance =
                    balance + ?
                WHERE id = ?
            ]],
            {
                amount,
                receiverId
            }
        }
    })

㉖ Transaction ใช้กับอะไรบ้าง

เหมาะกับ

Transfer Money
ซื้อรถ
ซื้อบ้าน
โอน Ownership
Craft Item
ซื้อสินค้า + ตัดเงิน
Business Payment

เมื่อมีหลาย Database Changes ที่ควรสำเร็จพร้อมกัน

㉗ .await คืออะไร

ตัวอย่าง

local row =
    MySQL.single.await(
        query,
        params
    )

หมายถึง Lua Coroutine จะรอ Result ก่อนดำเนิน Code ต่อ

ทำให้เขียน Code เป็นลำดับได้ง่าย

Query
↓
ได้ Result
↓
Validate
↓
ทำงานต่อ

โดยไม่ต้องเขียน Callback ซ้อนหลายชั้น

㉘ .await เท่ากับ Blocking FXServer ทั้งเครื่องไหม

ไม่ควรตีความแบบนั้น

await ทำงานร่วมกับ Citizen coroutine/scheduler

คือ Coroutine ปัจจุบันรอ Promise/Result

ไม่ใช่การ Freeze FXServer Process แบบ Blocking Loop ที่ไม่ Yield

แต่ Query จำนวนมากหรือ Query ช้ายังสร้าง Load และ Latency ได้

㉙ oxmysql รองรับ Callback ไหม

รองรับ

ตัวอย่าง

MySQL.single(
    [[
        SELECT id, cash
        FROM characters
        WHERE id = ?
        LIMIT 1
    ]],
    {
        characterId
    },
    function(player)

        if not player then
            return
        end

        print(
            player.cash
        )
    end
)

Developer เลือก Callback หรือ Await ตาม Code Style และ Use Case

㉚ Await กับ Callback แบบไหนดีกว่า

ไม่มีคำตอบตายตัว

await เหมาะกับ Code ที่ต้องทำตามลำดับ

Query
↓
Validation
↓
Action

Callback เหมาะกับ Event-driven Flow บางแบบ

สิ่งสำคัญกว่าคือ Code ต้อง

อ่านง่าย
จัด Error ได้
ไม่ Query เกินจำเป็น

㉛ Parameterized Query คืออะไร

แทนที่จะประกอบ SQL String

ไม่ควรทำ

local sql =
    "SELECT * FROM users WHERE name = '"
    .. name
    .. "'"

ควรใช้

MySQL.single.await(
    [[
        SELECT *
        FROM users
        WHERE name = ?
        LIMIT 1
    ]],
    {
        name
    }
)

ค่า name ถูกส่งแยกจาก SQL Structure

㉜ ทำไม Parameterized Query สำคัญ

ช่วยลดความเสี่ยง SQL Injection แบบทั่วไป และทำให้ Code อ่านง่ายขึ้น

Pattern คือ

SQL Structure
+
Parameters

แยกจากกัน

ไม่ใช่

SQL
+
Client Input
+
String Concatenation

㉝ Placeholder แบบ ? ใช้อย่างไร

ตัวอย่าง

SELECT *
FROM vehicles
WHERE character_id = ?
  AND stored = ?

Parameters

{
    characterId,
    1
}

ค่าจะถูก Bind ตามลำดับ

? #1 → characterId
? #2 → 1

㉞ Named Placeholders มีไหม

oxmysql มีระบบ Parameter Handling ที่รองรับรูปแบบต่าง ๆ ตาม API/Driver

แต่ Resource ใหม่ควรใช้ Syntax ที่ตรงกับ Documentation และ Version ที่ใช้งานจริง

เพื่อหลีกเลี่ยงการ Copy Syntax เก่าจาก mysql-async โดยไม่เข้าใจ

㉟ oxmysql รองรับ mysql-async เดิมไหม

หนึ่งในเหตุผลสำคัญที่ oxmysql ถูกใช้งานกว้างคือมี Compatibility Layer สำหรับ Code ที่มาจาก mysql-async

Library MySQL.lua มี Aliases เช่นแนวคิด

MySQL.Async.fetchAll
→ MySQL.query

และ

MySQL.Sync.fetchAll
→ MySQL.query.await

ช่วยให้ Resource เก่า Migration ได้ง่ายขึ้น

㊱ ghmattimysql ล่ะ

oxmysql ถูกสร้างมาเป็นตัวแทนทั้ง

mysql-async
ghmattimysql

พร้อม Expanded API

จึงช่วยลดกรณี Server ต้องใช้ Database Wrappers หลายตัวพร้อมกัน

㊲ ควรติดตั้ง mysql-async กับ oxmysql พร้อมกันไหม

ถ้า Resources สามารถใช้ Compatibility ของ oxmysql ได้แล้ว โดยทั่วไปไม่ควรเปิด Database Middleware หลายตัวโดยไม่มีเหตุผล

ต้องตรวจ Resource Dependencies ก่อน Migration

อย่าลบ Database Resource เก่าใน Production ทันทีโดยไม่ทดสอบ Script ทั้งหมด

㊳ ทำไม Script FiveM จำนวนมากใช้ oxmysql

เหตุผลหลักคือ

① API ครบ
② Lua await ใช้ง่าย
③ รองรับ JavaScript
④ Parameterized Queries
⑤ Prepared Queries
⑥ Transactions
⑦ Slow Query Debugging
⑧ Compatibility กับระบบเก่า
⑨ Framework Ecosystem รองรับกว้าง
⑩ ใช้ MySQL/MariaDB ได้

จึงเหมาะทั้ง Resource เล็กและ Framework Server ขนาดใหญ่

㊴ ใช้กับ ESX ได้ไหม

ได้

ESX Resources จำนวนมากต้องใช้ Database สำหรับ

Users
Jobs
Vehicles
Inventory
Accounts

และสามารถใช้ oxmysql เป็น Database Layer ได้ตาม Version/Architecture ของ ESX Resource ที่ใช้

㊵ ใช้กับ QBCore ได้ไหม

ได้

QBCore Documentation และ Recipes จำนวนมากใช้ oxmysql เป็น Database Wrapper

Flow คือ

QBCore
↓
Server Resources
↓
oxmysql
↓
Database

แต่ต้องใช้ Version ที่ Framework/Resources รองรับ

㊶ ใช้กับ Qbox ได้ไหม

ได้เช่นกัน

Qbox/OX Ecosystem มีความสัมพันธ์กับ Libraries กลุ่ม OX อย่างมาก

แต่ oxmysql ไม่ได้ผูกกับ Framework ใด Framework หนึ่ง

Standalone Resource ก็สามารถใช้ได้

㊷ ใช้แบบ Standalone ได้ไหม

ได้

ตัวอย่าง Resource ง่าย ๆ

player_notes

ไม่ต้องใช้ ESX/QB/Qbox

เพียง

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'

จากนั้น Query Database ได้โดยตรง

㊸ oxmysql รองรับ JavaScript ไหม

รองรับ

Package ปัจจุบันอธิบายตัวเองว่าเป็นการสื่อสารระหว่าง FXServer กับ MySQL ผ่าน node-mysql2

JavaScript Resources สามารถใช้ Exports หรือ Package/Type Support ตาม Documentation ได้

ดังนั้นไม่ได้จำกัดเฉพาะ Lua Developer

㊹ oxmysql ใช้ mysql2 หรือไม่

ใช่ Project ปัจจุบันระบุว่าเป็น

FXServer to MySQL communication
via node-mysql2

และ Package มี Dependency กับ mysql2

นี่คือ Database Driver Layer ภายในของ oxmysql

Developer Resource ทั่วไปไม่จำเป็นต้องเรียก mysql2 เอง

㊺ MySQL หรือ MariaDB เลือกอะไร

Documentation oxmysql แนะนำ MariaDB เพื่อ Compatibility กับ Resources FiveM จำนวนมากที่เดิมออกแบบจาก MySQL 5.7-era behavior

MySQL 8 ก็รองรับ แต่ Resource เก่าบางตัวอาจพบ Compatibility เช่น Reserved Keywords หรือ Schema Differences

ดังนั้นต้องดู Server Stack จริง

㊻ XAMPP ควรใช้ไหม

Documentation oxmysql ไม่แนะนำให้ติดตั้ง XAMPP เพียงเพื่อรัน Database

XAMPP เป็น Web Development Stack ที่รวมหลาย Service

สำหรับ Production FiveM Database ควรติดตั้ง Database Server โดยตรง เช่น MariaDB ตาม Architecture ที่เหมาะสม

㊼ Slow Query Warning คืออะไร

oxmysql สามารถเตือนเมื่อ Query ใช้เวลานานเกิน Threshold

ตัวอย่าง Config

set mysql_slow_query_warning 150

หมายถึง Threshold ตามหน่วยที่ Library กำหนดสำหรับ Warning

ช่วยหา Query ที่อาจต้องตรวจ Performance

㊽ Slow Query แปลว่า SQL แย่เสมอไหม

ไม่

Documentation ระบุว่า Query Time อาจได้รับผลจาก

Server Hitch
Startup Load
Database Load
Hardware
Network

ดังนั้น Warning คือสัญญาณให้ตรวจ

ไม่ใช่หลักฐานว่า SQL Query เขียนผิดเสมอ

㊾ mysql_debug คืออะไร

สามารถเปิด Debug Queries

set mysql_debug true

เพื่อดู Queries ใน Server Console/Debug Tools ตามระบบ oxmysql

มีประโยชน์ตอน Development และหา Query ที่ยิงบ่อยเกินไป

㊿ ควรเปิด mysql_debug ตลอด Production ไหม

ไม่จำเป็น

Debug Logging จำนวนมากสามารถสร้าง Log Volume และ Noise

เปิดเมื่อต้องตรวจปัญหา หรือจำกัดเฉพาะ Resources ที่ต้องการ Debug ตาม Configuration ที่ oxmysql รองรับ

Production ควรเปิดเท่าที่จำเป็น

51 Query มากเกินไปแก้อย่างไร

สมมติ Resource ทำ

Player 200 คน
×
10 Queries ต่อวินาที
=
2,000 Queries/วินาที

อาจไม่จำเป็นเลย

ควรพิจารณา

Runtime Cache
Batch Query
Events
Indexes
ลด Polling
ลด N+1 Queries

ก่อนเพิ่ม Hardware

52 อย่า Query Database ทุก Tick

ไม่ควร

CreateThread(function()

    while true do

        Wait(0)

        MySQL.query.await(
            'SELECT * FROM users'
        )

    end
end)

นี่เป็น Database Architecture ที่ผิดอย่างชัดเจน

Database เหมาะกับ Persistent Data ไม่ใช่ Frame-level Gameplay State

53 oxmysql กับ Cache ควรใช้ร่วมกันอย่างไร

ตัวอย่าง

Player Login
↓
oxmysql SELECT
↓
Runtime Player Data
↓
Gameplay
↓
Save changes
↓
oxmysql UPDATE

ช่วยลด Query จำนวนมาก

แต่ Cache ต้องมี Save/Recovery Strategy ที่เหมาะสม

54 oxmysql ป้องกัน Cheat ไหม

ไม่

oxmysql ทำหน้าที่ Database Communication

ถ้า Server Event เขียนแบบนี้

RegisterNetEvent(
    'money:set',
    function(amount)

        MySQL.update.await(
            [[
                UPDATE characters
                SET cash = ?
                WHERE id = ?
            ]],
            {
                amount,
                getCharacterId(source)
            }
        )
    end
)

Client ยังสามารถพยายามส่ง amount ที่โกงได้

ปัญหาอยู่ที่ Server Validation

ไม่ใช่ oxmysql

55 Pattern ที่ถูกต้องสำหรับ Economy

ควรเป็น

Client
↓
ขอทำ Action

Server
↓
ตรวจ Player
↓
ตรวจ State
↓
ตรวจ Permission
↓
คำนวณ Amount เอง
↓
oxmysql
↓
Persist

Client ไม่ควรเป็น Source of Truth ของเงิน

56 oxmysql ทำให้ SQL Injection หายหมดไหม

ไม่ควรพูดว่าหาย 100%

Parameterized Queries ช่วยป้องกัน SQL Injection ในรูปแบบทั่วไปได้มาก

แต่ Developer ยังสามารถเขียน Dynamic SQL ที่อันตรายเองได้ เช่นนำ

table name
column name
ORDER BY
raw SQL fragments

จาก Untrusted Client Input ไปประกอบ Query

ต้อง Validate Architecture ทั้งระบบ

57 อย่าให้ Client ส่งชื่อ Table

ตัวอย่างอันตราย

RegisterNetEvent(
    'db:read',
    function(tableName)

        local query =
            'SELECT * FROM '
            .. tableName

        MySQL.query.await(
            query
        )
    end
)

ไม่ควรเปิด Generic Database API ให้ Client

Server Event ควรเป็น Business Action เช่น

garage:getVehicles

ไม่ใช่

database:runQuery

58 oxmysql Resource Restart ต้องระวังไหม

มาก

ถ้า Resources จำนวนมากพึ่ง oxmysql แล้วคุณ

restart oxmysql

กลาง Production

Queries/Consumers อาจได้รับผลกระทบ

บาง Resource อาจ Initialize Database API ตอน Start เพียงครั้งเดียว

การ Restart Critical Database Dependency จึงควรทำใน Maintenance Context ที่เหมาะสม

59 Checklist ก่อนใช้ oxmysql

ตรวจ

① Database Server ติดตั้งหรือยัง?
② MySQL หรือ MariaDB?
③ Database ถูกสร้างหรือยัง?
④ Database User มีหรือยัง?
⑤ Password ปลอดภัยหรือไม่?
⑥ Connection String ถูกหรือไม่?
⑦ mysql_connection_string อยู่ก่อน oxmysql หรือไม่?
⑧ oxmysql Start ก่อน Consumer หรือไม่?
⑨ Resource ประกาศ dependency หรือไม่?
⑩ MySQL.lua Import ก่อน Server Scripts หรือไม่?
⑪ ใช้ query กับหลาย Rows ถูกไหม?
⑫ ใช้ single กับหนึ่ง Row ถูกไหม?
⑬ ใช้ scalar กับหนึ่ง Value ถูกไหม?
⑭ ใช้ insert เมื่ออยากได้ Insert ID หรือไม่?
⑮ ใช้ update เมื่ออยากได้ affected rows หรือไม่?
⑯ Transaction ใช้เมื่อ Operation ต้อง Atomic หรือไม่?
⑰ Query ใช้ Parameters หรือไม่?
⑱ มี SQL String Concatenation จาก Client หรือไม่?
⑲ Client Input Validate หรือยัง?
⑳ Query อยู่ใน Loop หนักหรือไม่?
㉑ มี N+1 Query หรือไม่?
㉒ Index เหมาะหรือไม่?
㉓ Slow Query Warning ถูกตรวจหรือไม่?
㉔ Debug เปิดโดยมีเหตุผลหรือไม่?
㉕ Backup Database มีหรือไม่?

⑥⓪ ตัวอย่าง Resource ใช้ oxmysql แบบพื้นฐาน

โครงสร้าง

com_players/
├── fxmanifest.lua
└── server/
    └── main.lua

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

author 'comsiam'
description 'FiveM oxmysql example'
version '1.0.0'

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

server_script 'server/main.lua'

server.cfg

set mysql_connection_string "mysql://username:password@localhost:3306/fivem"

ensure oxmysql
ensure com_players

server/main.lua

local function getCharacter(
    characterId
)

    if type(characterId)
        ~= 'number' then
        return nil
    end

    local character =
        MySQL.single.await(
            [[
                SELECT
                    id,
                    firstname,
                    lastname,
                    cash,
                    bank
                FROM characters
                WHERE id = ?
                LIMIT 1
            ]],
            {
                characterId
            }
        )

    return character
end

ตัวอย่าง Command

RegisterCommand(
    'dbtest',
    function(source)

        if source <= 0 then
            return
        end

        local characterId = 1

        local character =
            getCharacter(
                characterId
            )

        if not character then

            print(
                'Character not found'
            )

            return
        end

        print(
            character.firstname,
            character.lastname,
            character.cash
        )
    end,
    false
)

Flow คือ

/dbtest
↓
Server Resource
↓
MySQL.single.await
↓
oxmysql
↓
MariaDB/MySQL
↓
SELECT Row
↓
Lua Table
↓
Server Logic

นี่คือพื้นฐานที่ Resource Database FiveM จำนวนมากใช้

ตัวอย่างใช้ query

local vehicles =
    MySQL.query.await(
        [[
            SELECT
                id,
                plate,
                model,
                stored
            FROM vehicles
            WHERE character_id = ?
        ]],
        {
            characterId
        }
    )

แล้ว

for i = 1, #vehicles do

    local vehicle =
        vehicles[i]

    print(
        vehicle.id,
        vehicle.plate
    )
end

เหมาะกับหลาย Rows

ตัวอย่างใช้ scalar

local vehicleCount =
    MySQL.scalar.await(
        [[
            SELECT COUNT(*)
            FROM vehicles
            WHERE character_id = ?
        ]],
        {
            characterId
        }
    )

print(
    'Vehicles:',
    vehicleCount
)

เหมาะกับค่าหนึ่งค่า

ตัวอย่างใช้ insert

local id =
    MySQL.insert.await(
        [[
            INSERT INTO vehicles (
                character_id,
                plate,
                model
            )
            VALUES (?, ?, ?)
        ]],
        {
            characterId,
            plate,
            model
        }
    )

if not id then
    return
end

print(
    'Vehicle DB ID:',
    id
)

ตัวอย่างใช้ update

local changed =
    MySQL.update.await(
        [[
            UPDATE vehicles
            SET stored = ?
            WHERE id = ?
              AND character_id = ?
        ]],
        {
            1,
            vehicleId,
            characterId
        }
    )

if changed < 1 then
    return
end

print(
    'Vehicle stored'
)

Notice ว่า Query ตรวจ

id
+
character_id

ไม่ใช่ Update ด้วย Vehicle ID ที่ Client ส่งมาอย่างเดียว

ทำไม oxmysql ถึงสำคัญกับ FiveM Developer

เพราะเมื่อ Script โตเกิน

Command
Marker
Local Effect

เกือบทุก Roleplay System จะเริ่มต้องการ Persistence

เช่น

สร้าง Character
↓
Database

ซื้อรถ
↓
Database

เก็บรถ
↓
Database

ซื้อบ้าน
↓
Database

เปลี่ยน Job
↓
Database

ดังนั้น Developer ที่เขียน Resource จริงควรเข้าใจทั้ง

FiveM Server Logic
+
SQL
+
oxmysql API
+
Database Design

ไม่ควรเรียนแค่การ Copy Query จาก Script อื่น

คำถามที่พบบ่อยเกี่ยวกับ oxmysql FiveM

oxmysql FiveM คืออะไร

คือ Resource/Library ที่เชื่อม FXServer กับ MySQL หรือ MariaDB

oxmysql เป็น Database ไหม

ไม่ เป็น Database Connector

ต้องติดตั้ง MySQL หรือ MariaDB ด้วยไหม

ต้องมี Database Server ที่ oxmysql สามารถเชื่อมต่อได้

oxmysql ใช้กับ MariaDB ได้ไหม

ได้ และ Documentation แนะนำ MariaDB เพื่อ Compatibility กับ FiveM Resources จำนวนมาก

ใช้กับ MySQL 8 ได้ไหม

ได้ โดย oxmysql ถูกออกแบบพร้อม Compatibility ที่ดีขึ้นสำหรับ MySQL 8 แต่ Resource เก่าบางตัวอาจมี Schema/SQL Compatibility Issues

oxmysql ใช้กับ ESX ได้ไหม

ได้หาก Resource/Version นั้นรองรับ

ใช้กับ QBCore ได้ไหม

ได้ และพบใน QBCore Stack จำนวนมาก

ใช้กับ Qbox ได้ไหม

ได้

ใช้ Standalone ได้ไหม

ได้

oxmysql ต้อง Start ก่อน Core ไหม

ถ้า Core ต้องใช้ Database ควรให้ oxmysql พร้อมก่อน

Manifest เขียนอย่างไร

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

Connection String อยู่ตรงไหน

โดยทั่วไปตั้งใน server.cfg ก่อน Start oxmysql

MySQL.query ใช้ทำอะไร

ใช้เมื่อ Query คืนหลาย Rows/Result Set

MySQL.single ใช้ทำอะไร

คืน Row เดียว

MySQL.scalar ใช้ทำอะไร

คืนค่า Column แรกจาก Row เดียว

MySQL.insert ใช้ทำอะไร

เหมาะกับ Insert และคืน Insert ID

MySQL.update ใช้ทำอะไร

คืนจำนวน Rows ที่ได้รับผลจาก Operation

MySQL.prepare ใช้ทำอะไร

Prepared Query API สำหรับ Query ที่เหมาะสม

MySQL.transaction ใช้ทำอะไร

ทำหลาย Database Operations ใน Transaction เดียว

.await คืออะไร

รอ Result ใน Lua Coroutine แล้วจึงทำ Code ต่อ

Callback ยังใช้ได้ไหม

ได้

ใช้ await หรือ callback ดี

เลือกตาม Architecture; await มักทำให้ Sequential Database Logic อ่านง่าย

Parameter Placeholder ใช้อะไร

เช่น

WHERE id = ?

แล้วส่ง Parameters แยก

oxmysql ป้องกัน SQL Injection ไหม

Parameterized Queries ช่วยลดความเสี่ยงรูปแบบทั่วไป แต่ Developer ยังต้องไม่สร้าง Dynamic SQL จาก Untrusted Input โดยไม่ตรวจ

oxmysql ป้องกัน Cheat ไหม

ไม่ Server Validation ยังจำเป็น

Query Database ทุก Tick ได้ไหม

ไม่ควร

Slow Query Warning คืออะไร

ระบบเตือน Queries ที่ใช้เวลานานเกิน Threshold

Slow Query แปลว่า Database พังไหม

ไม่เสมอ Server Hitch และ Startup Load ก็มีผล

mysql_debug ใช้ทำอะไร

ช่วย Debug Queries ที่ Resources ส่งผ่าน oxmysql

oxmysql ใช้แทน mysql-async ได้ไหม

มี Compatibility Layer และถูกออกแบบมาเป็น Replacement สำหรับ mysql-async/ghmattimysql แต่ Resource จริงควรถูกทดสอบก่อน Migration

ควรรัน mysql-async และ oxmysql พร้อมกันไหม

ไม่ควรเพิ่ม Database Wrappers หลายตัวโดยไม่มีเหตุผล หาก Resources สามารถรวมมาใช้ oxmysql ได้ควรออกแบบ Stack ให้ชัดเจน

สรุป oxmysql FiveM คืออะไร ทำไม Script จำนวนมากถึงใช้

oxmysql คือ Database Connector สำหรับ FXServer ที่ทำให้ FiveM Server Scripts ติดต่อ MySQL/MariaDB ได้ผ่าน API ที่ออกแบบมาใช้งานสะดวกกับ Resource Architecture

มันอยู่ตรงกลางระหว่าง

FiveM Server Scripts
↓
oxmysql
↓
MySQL / MariaDB

และให้ APIs สำคัญอย่าง

query
single
scalar
insert
update
prepare
transaction

ทำให้ Developer สามารถเลือก Method ให้เหมาะกับ Result ที่ต้องการ

หลาย Rows
→ query

หนึ่ง Row
→ single

หนึ่ง Value
→ scalar

สร้าง Row + ต้องการ ID
→ insert

แก้ข้อมูล + ต้องการ affected rows
→ update

หลาย Operations ต้อง Atomic
→ transaction

อีกเหตุผลที่ oxmysql ถูกใช้อย่างกว้างใน FiveM Ecosystem คือมี Compatibility กับ APIs รุ่นเก่าอย่าง mysql-async และ ghmattimysql ทำให้ Resource จำนวนมากสามารถ Migration หรือใช้งานร่วมกับ Stack ปัจจุบันได้ง่ายขึ้น

สำหรับ Resource Lua โครงสร้างที่พบได้บ่อยคือ

dependency 'oxmysql'

server_script '@oxmysql/lib/MySQL.lua'

แล้วใน Server Code

local player =
    MySQL.single.await(
        [[
            SELECT id, cash
            FROM characters
            WHERE id = ?
            LIMIT 1
        ]],
        {
            characterId
        }
    )

แต่สิ่งสำคัญที่สุดไม่ใช่เพียงจำ Method ของ oxmysql

Developer ต้องเข้าใจด้วยว่า

Client
≠ Database Authority

Client ควรส่งเพียง Request

Server เป็นผู้

Validate
Calculate
Authorize
Query
Update

ข้อมูล Database

สำหรับผู้ที่เรียน FiveM Developer กับ comsiam การใช้ oxmysql ที่ดีจึงไม่ได้หมายถึง Query ได้เยอะที่สุด แต่คือ Query เท่าที่จำเป็น ใช้ Parameters อย่างถูกต้อง ออกแบบ Index และ Transaction ให้เหมาะ และแยก Runtime State ออกจาก Persistent State อย่างชัดเจน

หลักสำคัญจาก comsiam คือ อย่ามอง oxmysql เป็นที่เก็บข้อมูล แต่ให้มองมันเป็นสะพาน — ถ้า Database Schema, SQL หรือ Server Validation ออกแบบไม่ดี ต่อให้ Connector เร็วเพียงใด ระบบก็ยังช้า ผิดพลาด หรือไม่ปลอดภัยได้

หัวข้อถัดไปคือ วิธีเชื่อม FiveM กับ MySQL หรือ MariaDB ด้วย oxmysql ซึ่งจะทำแบบ Step-by-step ตั้งแต่สร้าง Database/User, ตั้ง mysql_connection_string, ติดตั้ง oxmysql, จัด server.cfg, เขียน fxmanifest.lua และทดสอบ SELECT 1 จนรู้แน่นอนว่า FXServer เชื่อม Database สำเร็จ

Comments

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด