WDO MySQL — Transacciones¶
WDO soporta transacciones explícitas con BeginTrans / Commit / Rollback y un método de conveniencia Transaction(bCode) que gestiona el commit/rollback automáticamente.
BeginTrans / Commit¶
El patrón básico para un flujo que siempre tiene éxito:
LOCAL oConn := WDO_Get( "mysql" )
IF oConn == NIL ; RETURN USendError( 503, "DB unavailable" ) ; ENDIF
oConn:BeginTrans()
oConn:Exec( "UPDATE accounts SET balance = balance - 200 WHERE owner = 'alice'" )
oConn:Exec( "UPDATE accounts SET balance = balance + 200 WHERE owner = 'bob'" )
oConn:Commit()
oConn:Close()
BeginTrans / Rollback¶
Cuando hay una condición de negocio que obliga a deshacer:
oConn:BeginTrans()
oConn:Exec( "UPDATE accounts SET balance = balance - 5000 WHERE owner = 'alice'" )
// Comprobar saldo provisional dentro de la transacción
LOCAL oStmt := oConn:Query( "SELECT balance FROM accounts WHERE owner = 'alice'" )
LOCAL hRow := oStmt:Fetch_Assoc()
oStmt:Free()
IF Val( hRow[ "balance" ] ) < 0
oConn:Rollback() // deshacer el UPDATE
RETURN USendError( 422, "Saldo insuficiente" )
ELSE
oConn:Commit()
ENDIF
oConn:Close()
TRY / FINALLY — patrón recomendado¶
En handlers reales, combina BeginTrans con TRY / FINALLY para garantizar que la transacción siempre se cierra aunque salte una excepción:
FUNCTION TransferFunds()
LOCAL oConn := WDO_Get( "mysql" )
LOCAL nFrom := Val( UPost( "from_id", "0" ) )
LOCAL nTo := Val( UPost( "to_id", "0" ) )
LOCAL nAmount:= Val( UPost( "amount", "0" ) )
LOCAL oErr := NIL
LOCAL lOk := .F.
IF oConn == NIL ; RETURN USendError( 503, "DB unavailable" ) ; ENDIF
oConn:BeginTrans()
TRY
oConn:Exec( "UPDATE accounts SET balance = balance - " + ;
hb_NToS( nAmount ) + " WHERE id = " + hb_NToS( nFrom ) )
oConn:Exec( "UPDATE accounts SET balance = balance + " + ;
hb_NToS( nAmount ) + " WHERE id = " + hb_NToS( nTo ) )
oConn:Commit()
lOk := .T.
CATCH oErr
oConn:Rollback()
le( "TransferFunds: " + oErr:description )
FINALLY
oConn:Close()
END
IF ! lOk
RETURN USendError( 500, "Transfer failed" )
ENDIF
RETURN USendJson( { "ok" => .T. } )
Transaction(bCode) — azúcar sintáctico¶
oConn:Transaction(bCode) hace todo el ciclo automáticamente:
- Llama
BeginTrans. - Evalúa el codeblock.
- Si termina sin excepción →
Commit. - Si se lanza una excepción →
Rollbacky re-lanza la excepción para que el caller la gestione.
LOCAL oConn := WDO_Get( "mysql" )
IF oConn == NIL ; RETURN USendError( 503, "DB unavailable" ) ; ENDIF
TRY
oConn:Transaction( {| o | ;
o:Exec( "UPDATE accounts SET balance = balance - 200 WHERE owner = 'alice'" ), ;
o:Exec( "UPDATE accounts SET balance = balance + 200 WHERE owner = 'bob'" ) ;
} )
CATCH oErr
oConn:Close()
RETURN USendError( 500, oErr:description )
END
oConn:Close()
USendJson( { "ok" => .T. } )
Transacciones con Prepared Statements¶
Los prepared statements son compatibles con transacciones — viven en la misma conexión:
oConn:BeginTrans()
TRY
LOCAL oStmt := oConn:Prepare( "INSERT INTO orders (uid, total) VALUES (?, ?)" )
oStmt:BindParam( 1, nUid, "i" )
oStmt:BindParam( 2, nTotal, "n" )
oStmt:Execute()
LOCAL nOrderId := oConn:Last_Insert_Id()
oStmt:Free()
oStmt := oConn:Prepare( "INSERT INTO order_items (order_id, sku) VALUES (?, ?)" )
FOR EACH cSku IN aSkus
oStmt:BindParam( 1, nOrderId, "i" )
oStmt:BindParam( 2, cSku, "s" )
oStmt:Execute()
NEXT
oStmt:Free()
oConn:Commit()
CATCH oErr
IF oStmt != NIL ; oStmt:Free() ; ENDIF
oConn:Rollback()
END
oConn:Close()
Rollback automático en el pool¶
Si un handler adquiere una conexión, abre una transacción y llama oConn:Close() sin hacer Commit ni Rollback explícito (o si el dispatcher reclama la conexión por el safety net), el pool hace Rollback automático antes de liberar el slot. Los datos nunca quedan en un estado parcial en el servidor.
Estado de la transacción¶
Útil para diagnóstico o para handlers que necesitan saber si están dentro de un contexto transaccional.
Referencia rápida¶
| Método | Descripción |
|---|---|
oConn:BeginTrans() |
Inicia la transacción (SET autocommit=0 + BEGIN) |
oConn:Commit() |
Confirma los cambios y cierra la transacción |
oConn:Rollback() |
Deshace los cambios y cierra la transacción |
oConn:Transaction(bCode) |
Azúcar: begin + eval + commit/rollback automático |
oConn:InTransaction() |
.T. si hay transacción abierta |