Minimaler Code zum zuverlässigen Speichern von Java-Objekten in einer Datei

8

In meiner kleinen, eigenständigen Java-Anwendung möchte ich Informationen speichern.

Meine Anforderungen:

  • lese und schreibe Java-Objekte (Ich möchte nicht SQL verwenden, und auch Abfragen ist nicht erforderlich)
  • einfach zu bedienen
  • einfach einzurichten
  • minimale externe Abhängigkeiten

Ich möchte daher jaxb verwenden, um alle Informationen in einer einfachen XML-Datei im Dateisystem zu speichern . Meine Beispielanwendung sieht so aus (kopiere den gesamten Code in eine Datei namens Application.java und kompiliere, keine zusätzlichen Anforderungen! !):

%Vor%

Wie kann ich diese Nachteile überwinden?

  • Das mehrmalige Starten der Anwendung kann zu Inkonsistenzen führen: Mehrere Benutzer können die Anwendung auf einem Netzlaufwerk ausführen und Concurrency Probleme
  • auftreten
  • Das Abbrechen des Schreibvorgangs kann zu beschädigten Daten oder zum Verlust aller Daten
  • führen
slartidan 18.02.2016, 10:43
quelle

5 Antworten

2

Um Ihre drei Probleme zu beantworten, die Sie erwähnt haben:

Ein mehrmaliges Starten der Anwendung kann zu Inkonsistenzen führen

Warum würde es zu Inkonsistenzen führen? Wenn das, was Sie meinen, mehrere gleichzeitige Bearbeitung ist, führt zu Inkonsistenzen, Sie müssen nur die Datei vor der Bearbeitung sperren. Der einfachste Weg, eine Sperrdatei neben der Datei zu erstellen. Überprüfen Sie vor dem Bearbeiten, ob eine Sperrdatei existiert.

Wenn Sie es fehlertoleranter machen möchten, können Sie auch eine Zeitüberschreitung für die Datei festlegen. z.B. Eine Sperrdatei ist 10 Minuten lang gültig. Sie könnten eine zufällig erzeugte UUID in die Lockdatei schreiben und vor dem Speichern könnten Sie überprüfen, ob die UUID stil übereinstimmt.

Mehrere Benutzer können die Anwendung auf einem Netzlaufwerk ausführen und Probleme mit Nebenläufigkeit auftreten

Ich denke, das ist das Gleiche wie Nummer 1.

Das Abbrechen des Schreibvorgangs kann zu beschädigten Daten oder dem Verlust aller Daten führen

Dies kann gelöst werden, indem der Schreibatom oder die Datei unveränderlich gemacht wird. Um es atomar zu machen, kopieren Sie die Datei direkt, anstatt die Datei direkt zu bearbeiten, und bearbeiten Sie sie auf der Kopie. Nachdem die Kopie gespeichert wurde, benennen Sie einfach die Dateien um. Aber wenn Sie auf der sicheren Seite sein möchten, könnten Sie immer Dinge wie den Zeitstempel an die Datei anhängen und niemals eine Datei bearbeiten oder löschen. Jedes Mal, wenn eine Bearbeitung vorgenommen wird, erstellen Sie eine Kopie davon, wobei ein neuerer Zeitstempel an die Datei angehängt wird. Und zum Lesen lesen Sie immer das Neueste.

    
Rowanto 29.02.2016, 21:58
quelle
7

Ihre Anforderungen sehen:

  • Starten Sie die Anwendung mehrmals
  • Mehrere Benutzer können die Anwendung auf einem Netzlaufwerk ausführen
  • Schutz vor Datenkorruption

Ich glaube, dass ein XML-basiertes Dateisystem nicht ausreichen wird. Wenn Sie eine richtige relationale Datenbank als Overkill betrachten, können Sie trotzdem eine H2 db auswählen. Dies ist eine superleichte db, die all diese oben genannten Probleme löst (wenn auch nicht perfekt, aber sicherlich viel besser als eine handgeschriebene XML-db) und dennoch sehr einfach einzurichten und zu warten ist.

Sie können es so konfigurieren, dass Ihre Änderungen auf der Festplatte beibehalten werden. Es kann so konfiguriert werden, dass es als eigenständiger Server ausgeführt wird und mehrere Verbindungen akzeptiert oder als Teil Ihrer Anwendung im Embedded-Modus ausgeführt werden kann.

Zum "Wie speichern Sie die Daten" :

Wenn Sie keine erweiterte ORM-Bibliothek (wie Hibernate oder eine andere JPA-Implementierung) verwenden möchten, können Sie immer noch einfach alte JDBC verwenden. Oder zumindest einige Spring-JDBC, die sehr leicht und einfach zu bedienen ist.

"Was speichern Sie?"

H2 ist eine relationale Datenbank. Also was auch immer du speicherst, es wird in Spalten enden. Aber! Wenn Sie wirklich nicht beabsichtigen, Ihre Daten abzufragen (und keine Migrationsskripte darauf anwenden), können Sie Ihre bereits XML-serialisierten Objekte speichern ist eine Option. Sie können einfach eine Tabelle mit einer ID + einer varchar-Spalte "data" definieren und dort Ihre XML-Datei speichern. Es gibt keine Begrenzung der Datenlänge in H2DB.

Hinweis: Das Speichern von XML in einer relationalen Datenbank ist im Allgemeinen keine gute Idee. Ich rate Ihnen nur, diese Option zu bewerten, weil Sie sicher sind, dass Sie nur eine bestimmte Menge von Funktionen benötigen, die eine SQL-Implementierung bereitstellen kann.

    
Gergely Bacso 18.02.2016 11:17
quelle
3

Inkonsistenzen und Parallelität werden auf zwei Arten behandelt:

  • durch Sperren
  • durch Versionierung

Beschädigtes Schreiben kann auf Anwendungsebene nicht gut behandelt werden. Das Dateisystem soll das Journaling unterstützen, das versucht, das bis zu einem gewissen Grad zu beheben. Sie können dies auch durch

tun
  • Erstellen Sie Ihre eigene Journaling-Datei (d. h. eine kurzlebige separate Datei mit Änderungen, die an die echte Datendatei übergeben werden sollen).

Alle diese Merkmale sind sogar in der einfachsten relationalen Datenbank verfügbar, z. H2, SQLite und sogar eine Webseite können solche Funktionen in HTML5 verwenden. Es ist ein ziemlicher Overkill, diese von Grund auf neu zu implementieren, und die richtige Implementierung der Datenspeicherschicht wird Ihre einfachen Anforderungen ziemlich kompliziert machen.

Aber nur für die Aufzeichnungen:

Concurrency-Behandlung mit Sperren

  • vor dem Start den xml zu ändern, eine Dateisperre verwenden, um einen exklusiven Zugriff auf die Datei zu erhalten, siehe auch Wie kann ich eine Datei mit Java (wenn möglich) sperren
  • Sobald die Aktualisierung abgeschlossen ist und Sie die Datei erfolgreich geschlossen haben, geben Sie die Sperre frei

Konsistenz (Atomizität) mit Sperren umgehen

  • Andere Anwendungsinstanzen versuchen möglicherweise immer noch, die Datei zu lesen, während eine der Anwendungen sie schreibt. Dies kann Inkonsistenzen verursachen (aka dirty-read). Stellen Sie sicher, dass der Writer-Prozess beim Schreiben eine exklusive Sperre für die Datei hat. Wenn es nicht möglich ist, eine exklusive Zugriffssperre zu erhalten, muss der Schreiber etwas warten und es erneut versuchen.

  • Eine Anwendung, die die Datei liest, muss sie lesen (wenn sie Zugriff erhält, keine anderen Instanzen eine exklusive Sperre), dann schließen Sie die Datei. Wenn das Lesen nicht möglich ist (wegen anderer App-Sperren), warten Sie und versuchen Sie es erneut.

  • noch eine externe Anwendung (z. B. Notepad) kann die XML ändern. Sie können beim Lesen der Datei eine exklusive Lesesperre bevorzugen.

Einfaches Journaling

Hier ist die Idee, wenn Sie viel schreiben müssen (oder wenn Sie später vielleicht Ihre Schreibvorgänge rückgängig machen wollen), wollen Sie die reale Datei nicht berühren. Stattdessen:

  • schreibt Änderungen an eine separate Journaldatei, die von Ihrer App-Instanz erstellt und gesperrt wird

  • Ihre App-Instanz sperrt die Hauptdatei nicht, sie sperrt nur die Journaldatei

  • , wenn alle Schreibvorgänge sind gut zu gehen, die App öffnet die eigentliche Datei mit exklusiver Schreibsperre, und verpflichtet jede Änderung in der Journaling-Datei, dann schließen Sie die Datei.

Wie Sie sehen, macht die Lösung mit Sperren die Datei zu einer freigegebenen Ressource, die durch Sperren geschützt ist und nur eine Anwendung gleichzeitig auf die Datei zugreifen kann. Dies behebt die Nebenläufigkeitsprobleme, macht aber auch den Dateizugriff zu einem Engpass. Daher verwenden moderne Datenbanken wie Oracle die Versionierung statt Sperren. Die Versionierung bedeutet, dass sowohl die alte als auch die neue Version der Datei zur gleichen Zeit verfügbar sind. Leser werden mit der alten, vollständigsten Datei bedient. Sobald das Schreiben der neuen Version abgeschlossen ist, wird es mit der alten Version zusammengeführt und die neuen Daten werden sofort verfügbar. Dies ist schwieriger zu implementieren, aber da es ermöglicht, die Zeit für alle Anwendungen parallel zu lesen, skaliert es viel besser.

    
Gee Bee 23.02.2016 17:27
quelle
2

Beachten Sie, dass Ihre einfache Antwort nicht gleichzeitige Schreibvorgänge von verschiedenen Instanzen behandelt. Wenn zwei Instanzen Änderungen vornehmen und speichern, werden durch das Auswählen des neuesten die Änderungen der anderen Instanz verloren gehen. Wie in anderen Antworten erwähnt, sollten Sie wahrscheinlich versuchen, Dateisperrung dafür zu verwenden.

eine relativ einfache Lösung:

  • Verwenden Sie eine separate Sperrdatei zum Schreiben von "data.xml.lck". Sperren Sie dies beim Schreiben der Datei
  • Wie in meinem Kommentar erwähnt, schreiben Sie zuerst in eine temporäre Datei "data.xml.tmp" und benennen Sie sie dann in den endgültigen Namen um, wenn der Schreibvorgang "data.xml" abgeschlossen ist. Dies gibt eine angemessene Sicherheit, dass jeder, der die Datei liest, eine vollständige Datei erhält.
  • selbst bei der Dateisperrung müssen Sie immer noch das "merge" -Problem behandeln (eine Instanz liest, eine andere schreibt, dann will die erste schreiben). Um damit umgehen zu können, sollten Sie eine Versionsnummer im Dateiinhalt haben. Wenn eine Instanz schreiben möchte, erwirbt sie zuerst die Sperre. dann überprüft es seine lokale Versionsnummer gegen die Versionsnummer der Datei. Wenn es veraltet ist, muss es zusammenführen, was in der Datei mit den lokalen Änderungen ist. dann kann es eine neue Version schreiben.
jtahlborn 27.02.2016 15:12
quelle
0

Nachdem ich eine Weile darüber nachgedacht habe, möchte ich versuchen, es so umzusetzen:

  • Öffnen Sie die data.<timestamp>.xml -Datei mit dem neuesten Zeitstempel.
  • Verwenden Sie nur den Modus readonly .
  • Änderungen vornehmen.
  • Speichern Sie die Datei als data.<timestamp>.xml - überschreiben Sie nicht und prüfen Sie, ob keine Datei mit neuerem Zeitstempel existiert.
slartidan 23.02.2016 13:27
quelle

Tags und Links