Transactions & Connection Management
OrionORM provides both high-level and low-level APIs for executing database operations, giving you fine-grained control over transactions and connection lifecycles.
1. Connection Pooling
dbConnection()
: checks out a connection from the pool (auto-initializes if empty).releaseConnection(conn)
: returns a connection to the pool or closes it if the pool is full.- Pool health: connections are validated (
SELECT 1
) before use; dropped ones are reconnected.
2. executeQuery
APIs
2.1 High-Level (One-Shot)
# Opens and closes the connection automatically.
result = executeQuery(
"SELECT * FROM users WHERE active = ?", [true]
)
Signature:
executeQuery(sql::String, params::Vector{Any}=Any[]; useTransaction::Bool=true)
Behavior:
- Checks out a connection (
dbConnection()
). - Prepares, executes, closes statement.
- Commits or rolls back DML if
useTransaction=true
. - Releases connection (
releaseConnection
).
- Checks out a connection (
2.2 Low-Level (Conn-Aware)
conn = dbConnection()
DBInterface.transaction(conn) do
executeQuery(conn, "UPDATE accounts SET balance = balance - ? WHERE id = ?", [100,1]; useTransaction=false)
executeQuery(conn, "UPDATE accounts SET balance = balance + ? WHERE id = ?", [100,2]; useTransaction=false)
end
releaseConnection(conn)
Signature:
executeQuery(conn::DBInterface.Connection, sql::String, params::Vector{Any}=Any[]; useTransaction::Bool=true)
Behavior:
- Uses your provided
conn
without closing it. - If
useTransaction=true
, wraps the single statement in its own transaction; otherwise executes raw. - Always closes the prepared statement in a
finally
block.
- Uses your provided
3. Best Practices
- One-shot queries: prefer the high-level API for isolated operations (
SELECT
, singleINSERT
, etc.). - Batch updates/inserts: open a connection once, wrap multiple
executeQuery(conn, ...; useTransaction=false)
calls insideDBInterface.transaction
, then release the connection. - Do not nest transactions: avoid
useTransaction=true
insideDBInterface.transaction
blocks—most drivers don’t support nested transactions. - Release in
finally
: always pairdbConnection()
withreleaseConnection(conn)
in atry
/finally
.
4. Example: Atomic Transfer
function transfer_funds(from_id::Int, to_id::Int, amount::Float64)
conn = dbConnection()
try
DBInterface.transaction(conn) do
executeQuery(conn,
"UPDATE accounts SET balance = balance - ? WHERE id = ?", [amount, from_id];
useTransaction=false
)
executeQuery(conn,
"UPDATE accounts SET balance = balance + ? WHERE id = ?", [amount, to_id];
useTransaction=false
)
end
finally
releaseConnection(conn)
end
end
- All updates occur in a single transaction; on error, changes are rolled back.
5. Troubleshooting
Commands out of sync
: ensure prepared statements are closed and connections are released promptly.- Zombie connections: monitor pool size and logs; improper
releaseConnection
usage can exhaust the pool. - Transaction deadlocks: use retries or reduced transaction scope if needed.