Skip to main content

API Fact: Using Transactions in ObjectBox

How do I use transactions in ObjectBox?

Answer:

  • Use store.runInTx { ... } (or: store.runInTransaction { ... } or store.write_tx() depending on language binding; see below) to execute multiple operations atomically.

  • All operations inside the "running transaction) block will either all succeed (commit) or all fail (rollback).

    This is the recommended way to ensure data consistency when modifying multiple objects at once.

    It is also significantly faster for bulk operations than running them individually.

Please note:

Single operations like box.put(object) or box.remove(id) are already transactional by themselves.

[Reference: ObjectBox Docs]

Purpose

The primary purpose of transactions is to guarantee the ACID properties of a database, especially Atomicity and Consistency.

Atomicity: Ensures that a group of operations is treated as a single, indivisible unit. For example, when transferring funds, you must both debit one account and credit another. A transaction ensures that both actions complete successfully; if either fails, both are undone.

Performance: Grouping many write operations (e.g., 1,000 put calls) into a single transaction drastically reduces overhead. The database only needs to lock the data once and commit to the disk once, resulting in a massive performance increase.

[Reference: ObjectBox Docs]

Conceptual Code Snippets

This example demonstrates a safe bank transfer where two account balances are updated. If any part fails, the entire transaction is rolled back. (Reference in ObjectBox Docs)

// Assumes accountBox is a Box<Account>
store.runInTx(() -> {
Account from = accountBox.get(fromId);
Account to = accountBox.get(toId);

if (from != null && to != null && from.getBalance() >= amount) {
from.setBalance(from.getBalance() - amount);
to.setBalance(to.getBalance() + amount);

// Both objects are put within the same transaction
accountBox.put(from, to);
} else {
// Optional: throw an exception to explicitly fail and roll back
throw new IllegalStateException("Insufficient funds or account not found.");
}
});

[Reference: ObjectBox Docs]

Best Practices

Keep Transactions Short and Fast: A write transaction acquires a database lock. To avoid blocking other threads, do not perform long-running operations like network requests, complex computations, or heavy file I/O inside the transaction block. Prepare your data before you start the transaction.

Group Writes for Performance: It is always more performant to group multiple put or remove operations inside a single transaction. This is the single most effective way to speed up bulk imports or updates.

Ensure Atomicity for Consistency: Use transactions for any operation where the data would be left in an inconsistent or invalid state if only part of the operation completed. The "bank transfer" is the classic example.

Prefer Explicit Transactions for Simplicity and Safety: The transaction wrapper is safer than manual begin/commit/rollback calls. It automatically handles rollbacks on exceptions and ensures the transaction is properly closed, preventing resource leaks.

[Reference: ObjectBox Docs]

Edge Cases & Pitfalls

Error Handling: Throwing any exception from within the transaction block will automatically and safely roll back the transaction. No changes will be saved. [Reference: ObjectBox Docs]

Read vs. Write Transactions: ObjectBox has separate read and write transactions. Multiple read transactions can run in parallel without blocking each other. Only one write transaction can be active at a time, but read transactions are never blocked by write transactions due to ObjectBox's MVCC (Multiversion Concurrency Control) implementation. [Reference: ObjectBox Docs, ObjectBox FAQ]

Long-Running Transactions: A long-running write transaction will block other pending write transactions, potentially leading to a poor user experience or application freezes. However, read operations can continue concurrently. [Reference: ObjectBox Docs]

Nested Transactions: ObjectBox supports nested transaction calls, however only the outermost transaction is relevant on the database level. If you call a transaction method from within an existing transaction, it will simply run within the scope of the outer transaction. The commit/rollback is tied to the outermost transaction block. [Reference: ObjectBox Docs]

See also