Start · Sprachen · JavaScript · Referenz · IDBTransaction

IDBTransaction

Schnittstelle

Stellt eine asynchrone Transaktion auf einer IndexedDB-Datenbank dar, die Lese- und Schreibzugriffe auf Object Stores bündelt und atomar abschließt.

seit JavaScript Baseline: alle modernen Browser (IndexedDB 2.0; du Kategorie: webapi

Signatur

interface IDBTransaction extends EventTarget

Beschreibung

IDBTransaction ist das zentrale Werkzeug für jeden Datenzugriff in IndexedDB: Lesen, Schreiben und Löschen passiert immer innerhalb einer Transaktion. Eine Transaktion erzeugt man mit IDBDatabase.prototype.transaction(storeNames, mode, options). Dabei legt man fest, auf welche Object Stores sie sich bezieht (ihren Scope) und in welchem Modus sie läuft: "readonly", "readwrite" oder "versionchange". Den Modus "versionchange" erzeugt nur der Browser selbst, und zwar im upgradeneeded-Ereignis.

Transaktionen sind atomar. Entweder werden alle Änderungen übernommen (Ereignis complete), oder keine davon (Ereignis abort). Schlägt eine einzelne Anfrage fehl und wird der Fehler nicht mit event.preventDefault() abgefangen, bricht die ganze Transaktion ab. Mit abort() macht man alle Änderungen ausdrücklich rückgängig. commit() schließt die Transaktion dagegen vorzeitig ab, ohne auf das automatische Ende zu warten.

Wichtige Eigenschaften und Methoden:

  • db: die zugehörige IDBDatabase
  • mode: "readonly", "readwrite" oder "versionchange"
  • objectStoreNames: eine DOMStringList mit den Stores im Scope
  • durability: "default", "strict" oder "relaxed"
  • error: eine DOMException, wenn die Transaktion abgebrochen wurde
  • objectStore(name): liefert einen IDBObjectStore aus dem Scope
  • abort() und commit()
  • Ereignisse: complete, abort und error, jeweils per addEventListener oder über oncomplete, onabort und onerror

Eine Transaktion bleibt nur so lange aktiv, wie in ihren Callbacks neue Anfragen gestellt werden. Gibt man die Kontrolle an die Ereignisschleife zurück, ohne eine offene Anfrage zu haben (etwa durch ein await auf fetch()), wird sie automatisch abgeschlossen. Danach wirft jeder weitere Zugriff einen TransactionInactiveError.

Parameter

Name Typ Default Beschreibung
$storeNames Pflicht string | string[] Parameter von IDBDatabase.transaction(): Name oder Liste der Object Stores, auf die die Transaktion zugreifen darf.
$mode "readonly" | "readwrite" "readonly" Zugriffsmodus. Mehrere readonly-Transaktionen dürfen parallel laufen. readwrite-Transaktionen mit überlappendem Scope laufen nacheinander.
$options { durability?: "default" | "strict" | "relaxed" } { durability: "default" } Mit "relaxed" meldet der Browser complete schneller, ohne vorher auf den Plattenschreibvorgang zu warten. "strict" erzwingt das Schreiben auf die Platte vor complete.

Rückgabewert

Typ
IDBTransaction
Beschreibung
IDBDatabase.prototype.transaction() gibt ein neues, aktives IDBTransaction-Objekt zurück.

Beispiele

Datensätze in einer readwrite-Transaktion speichern

const openDb = () => new Promise((resolve, reject) => {
  const req = indexedDB.open('shop', 1);
  req.onupgradeneeded = () => {
    req.result.createObjectStore('produkte', { keyPath: 'id' });
  };
  req.onsuccess = () => resolve(req.result);
  req.onerror = () => reject(req.error);
});

const db = await openDb();
const tx = db.transaction('produkte', 'readwrite');
const store = tx.objectStore('produkte');

store.put({ id: 1, name: 'Tastatur', preis: 49.9 });
store.put({ id: 2, name: 'Maus', preis: 19.9 });

tx.addEventListener('complete', () => console.log('Gespeichert, Modus:', tx.mode));
tx.addEventListener('error', () => console.error('Fehler:', tx.error));
Gespeichert, Modus: readwrite

Transaktion als Promise verpacken

const txDone = (tx) => new Promise((resolve, reject) => {
  tx.oncomplete = () => resolve();
  tx.onabort = () => reject(tx.error ?? new DOMException('Abgebrochen', 'AbortError'));
  tx.onerror = () => reject(tx.error);
});

const db = await new Promise((res, rej) => {
  const r = indexedDB.open('shop', 1);
  r.onupgradeneeded = () => r.result.createObjectStore('produkte', { keyPath: 'id' });
  r.onsuccess = () => res(r.result);
  r.onerror = () => rej(r.error);
});

const tx = db.transaction(['produkte'], 'readwrite', { durability: 'relaxed' });
tx.objectStore('produkte').delete(2);
await txDone(tx);
console.log('Löschen abgeschlossen');
Löschen abgeschlossen

Änderungen bei Validierungsfehler mit abort() verwerfen

const db = await new Promise((res, rej) => {
  const r = indexedDB.open('shop', 1);
  r.onupgradeneeded = () => r.result.createObjectStore('produkte', { keyPath: 'id' });
  r.onsuccess = () => res(r.result);
  r.onerror = () => rej(r.error);
});

const importiere = (produkte) => {
  const tx = db.transaction('produkte', 'readwrite');
  const store = tx.objectStore('produkte');
  tx.onabort = () => console.log('Import verworfen:', tx.error?.name ?? 'manuell');
  tx.oncomplete = () => console.log('Import erfolgreich');

  for (const p of produkte) {
    if (typeof p.preis !== 'number') {
      tx.abort(); // alle bisherigen put()-Aufrufe werden zurückgenommen
      return;
    }
    store.put(p);
  }
  tx.commit(); // sofort abschließen, nicht auf das automatische Ende warten
};

importiere([{ id: 3, name: 'Monitor', preis: 199 }, { id: 4, name: 'Kabel', preis: 'gratis' }]);
Import verworfen: manuell

// Wichtig · Fallstricke

  • Automatisches Abschließen: Zwischen zwei Anfragen darf man nicht auf Promises warten, die nichts mit IndexedDB zu tun haben (z. B. await fetch() oder setTimeout). Die Transaktion schließt sonst automatisch ab, und die nächste Anfrage wirft einen TransactionInactiveError. Externe Daten deshalb holen, bevor man die Transaktion öffnet.
  • success heißt noch nicht gespeichert: Das success-Ereignis einer einzelnen IDBRequest bedeutet nicht, dass die Daten dauerhaft geschrieben sind. Darauf verlassen kann man sich erst nach dem complete-Ereignis der Transaktion.
  • Fehler blubbern hoch: Das error-Ereignis einer Anfrage steigt zur Transaktion und danach zur Datenbank auf. Ohne preventDefault() führt es zum Abbruch (abort).
  • Scope: Wer objectStore() mit einem Store außerhalb des Scopes aufruft, bekommt einen NotFoundError. Wer in einer readonly-Transaktion schreiben will, bekommt einen ReadOnlyError.
  • Performance: readwrite-Transaktionen mit überlappendem Scope sperren sich gegenseitig. Den Scope daher so klein wie möglich halten und viele Schreibvorgänge in einer Transaktion bündeln, statt für jeden eine eigene zu öffnen.
  • Kompatibilität: IndexedDB selbst läuft in allen modernen Browsern und in Web Workern. commit() und durability gibt es erst in neueren Versionen (Chrome 76+/83+, Firefox 74+/126+, Safari 15+). Bei Bedarf vorher mit 'commit' in IDBTransaction.prototype prüfen. Im privaten Modus mancher Browser ist der Speicher eingeschränkt oder wird beim Schließen gelöscht.
  • Die früheren numerischen Konstanten (IDBTransaction.READ_ONLY usw.) sind veraltet. Stattdessen die String-Werte verwenden.