Transactions

Transactions & Raw Queries#

Transactions#

Wrap multiple operations in a transaction for atomicity:

await DuxtOrm.transaction((tx) async {
  // Create user
  await tx.query(
    'INSERT INTO users (email, name) VALUES (?, ?)',
    ['[email protected]', 'Test User'],
  );

  // Get the new user's ID
  final userId = await tx.lastInsertId();

  // Create profile
  await tx.query(
    'INSERT INTO profiles (user_id, bio) VALUES (?, ?)',
    [userId, 'Hello world!'],
  );

  // Create initial settings
  await tx.query(
    'INSERT INTO user_settings (user_id, theme) VALUES (?, ?)',
    [userId, 'dark'],
  );
});

Auto Commit/Rollback#

  • Success: Transaction auto-commits when the callback completes
  • Error: Transaction auto-rolls back if an exception is thrown
try {
  await DuxtOrm.transaction((tx) async {
    await tx.query('INSERT INTO accounts (balance) VALUES (?)', [1000]);
    await tx.query('UPDATE accounts SET balance = balance - ? WHERE id = ?', [500, 1]);

    // This will cause rollback
    throw Exception('Something went wrong');
  });
} catch (e) {
  print('Transaction rolled back: $e');
}

Manual Commit/Rollback#

For more control:

final tx = await DuxtOrm.beginTransaction();

try {
  await tx.query('INSERT INTO logs (message) VALUES (?)', ['Started']);
  await tx.query('UPDATE counters SET value = value + 1 WHERE name = ?', ['visits']);

  // Explicit commit
  await tx.commit();
} catch (e) {
  // Explicit rollback
  await tx.rollback();
  rethrow;
}

Raw Queries#

SELECT Queries#

final results = await DuxtOrm.raw(
  'SELECT * FROM users WHERE email = ?',
  ['[email protected]'],
);

for (final row in results) {
  print('${row['id']}: ${row['name']}');
}

Complex SELECT#

final results = await DuxtOrm.raw('''
  SELECT
    u.id,
    u.name,
    COUNT(p.id) as post_count
  FROM users u
  LEFT JOIN posts p ON p.user_id = u.id
  WHERE u.is_active = ?
  GROUP BY u.id
  HAVING post_count > ?
  ORDER BY post_count DESC
  LIMIT ?
''', [1, 5, 10]);

INSERT/UPDATE/DELETE#

Use execute() for statements that don't return rows:

// Returns number of affected rows
final affected = await DuxtOrm.execute(
  'UPDATE users SET is_active = ? WHERE last_login < ?',
  [0, '2023-01-01'],
);

print('Deactivated $affected users');

Bulk Insert#

final users = [
  ['[email protected]', 'John'],
  ['[email protected]', 'Jane'],
  ['[email protected]', 'Bob'],
];

for (final user in users) {
  await DuxtOrm.execute(
    'INSERT INTO users (email, name) VALUES (?, ?)',
    user,
  );
}

Or with a single statement (database-specific syntax):

// PostgreSQL
await DuxtOrm.execute('''
  INSERT INTO users (email, name) VALUES
  ('[email protected]', 'John'),
  ('[email protected]', 'Jane'),
  ('[email protected]', 'Bob')
''');

Parameter Binding#

Always use parameter binding to prevent SQL injection:

// GOOD - Parameters are escaped
await DuxtOrm.raw(
  'SELECT * FROM users WHERE email = ?',
  [userInput],
);

// BAD - SQL injection vulnerability
await DuxtOrm.raw(
  'SELECT * FROM users WHERE email = "$userInput"',  // DON'T DO THIS
);

Database-Specific Features#

PostgreSQL#

// RETURNING clause
final result = await DuxtOrm.raw(
  'INSERT INTO users (email, name) VALUES (\$1, \$2) RETURNING id, created_at',
  ['[email protected]', 'Test'],
);
final newId = result.first['id'];

// JSONB operations
await DuxtOrm.raw(
  "SELECT * FROM products WHERE metadata->>'category' = \$1",
  ['electronics'],
);

MySQL#

// Get last insert ID
await DuxtOrm.execute(
  'INSERT INTO users (email, name) VALUES (?, ?)',
  ['[email protected]', 'Test'],
);
final id = await DuxtOrm.lastInsertId();

// ON DUPLICATE KEY UPDATE
await DuxtOrm.execute('''
  INSERT INTO counters (name, value) VALUES (?, ?)
  ON DUPLICATE KEY UPDATE value = value + 1
''', ['page_views', 1]);

SQLite#

// UPSERT (SQLite 3.24+)
await DuxtOrm.execute('''
  INSERT INTO settings (key, value) VALUES (?, ?)
  ON CONFLICT(key) DO UPDATE SET value = excluded.value
''', ['theme', 'dark']);

// Get changes count
final changes = await DuxtOrm.raw('SELECT changes()');

Connection Management#

// Close connection when done
await DuxtOrm.close();

// Check if connected
if (DuxtOrm.isConnected) {
  // ...
}

// Reconnect if needed
await DuxtOrm.reconnect();