Input/Output Nur Änderungen speichern

sharkattack

Mitglied
Hallo zusammen

Ich schreibe im Moment gerade ein Adressbuch und es funktioniert eigentlich schon recht gut. Das einzige Problem ist, dass ich mir noch nicht sicher bin, wie ich die Daten speichern soll. Im Moment verwende ich die Methoden aus diesen Tutorial: http://code.makery.ch/library/javafx-8-tutorial/part5/
Dort werden die Daten als xml datei gespeichert und geladen und die Methoden dazu sehen so aus:
Java:
public void loadPersonDataFromFile(File file) {
    try {
        JAXBContext context = JAXBContext
                .newInstance(PersonListWrapper.class);
        Unmarshaller um = context.createUnmarshaller();

        // Reading XML from the file and unmarshalling.
        PersonListWrapper wrapper = (PersonListWrapper) um.unmarshal(file);

        personData.clear();
        personData.addAll(wrapper.getPersons());

        // Save the file path to the registry.
        setPersonFilePath(file);

    } catch (Exception e) { // catches ANY exception
        Alert alert = new Alert(AlertType.ERROR);
        alert.setTitle("Error");
        alert.setHeaderText("Could not load data");
        alert.setContentText("Could not load data from file:\n" + file.getPath());

        alert.showAndWait();
    }
}


public void savePersonDataToFile(File file) {
    try {
        JAXBContext context = JAXBContext
                .newInstance(PersonListWrapper.class);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        // Wrapping our person data.
        PersonListWrapper wrapper = new PersonListWrapper();
        wrapper.setPersons(personData);

        // Marshalling and saving XML to the file.
        m.marshal(wrapper, file);

        // Save the file path to the registry.
        setPersonFilePath(file);
    } catch (Exception e) { // catches ANY exception
        Alert alert = new Alert(AlertType.ERROR);
        alert.setTitle("Error");
        alert.setHeaderText("Could not save data");
        alert.setContentText("Could not save data to file:\n" + file.getPath());

        alert.showAndWait();
    }
}

Ich habe mein Programm so geschrieben, dass bei jeder Änderung (Eintrag hinzufügen, löschen) die Änderungen automatisch gespeichert werden.
Mit der Methode oben wird dann aber immer die komplette arraylist in die xml Datei geschrieben und nicht nur die vorgenommenen Änderungen. Bei einigen Einträgen wird dies ja kein Problem sein, aber ich denke mir, wenn mal ein paar tausend Kontakte in der Liste stehen, ist diese Methode nicht gerade im Sinne des Erfinders. Was denkt ihr dazu? Was würdet ihr empfehlen?

Beim Laden werden alle Daten in die Arraylist geladen. Meine Arraylist bestehlt aus etwa 30 Objekten, fast alles Strings. Ich meine mal gelesen zu haben, dass ein Objekt etwa 4 Byte Speicher benötigt. Dies wären dann bei tausend Eintragen: 1000 * 30 * 4 Byte = 120kb. Das ist ja eigentlich sehr wenig, wenn das mit den 4 Byte ungefähr stimmt. Hier sehe ich eigentlich keine Probleme, aber dass bei einer Änderung immer die komplette Datei neu geschrieben wird finde ich doch etwas unschön.

Für Tipps wäre ich euch sehr dankbar.

LG
 
Meine Arraylist bestehlt aus etwa 30 Objekten, fast alles Strings.
Du hast nur Listen als Datenstrukturen?
Dann würde ich erstmal anfangen, vernünftige Klassen zu nutzen 😉


Das Problem des Speicherns würde ich einfach erstmal aufschieben - wenn das Programm sinnvoll aufgebaut ist, kann man das Speichern ohne großen Aufwand umbauen, zB auf eine Datenbank
 
Danke erstmal für die Antworten. Gibt es irgendwo eine gute Einführung zu Datenbanken und Java? Kann man das dann trotzdem mit Arraylisten machen, oder muss man dann für alles direkt Datenbankbefehle benutzen?

@mrBrown
Was meinst du mit nur Listen? Vielleicht habe ich es etwas falsch beschrieben. Ich habe eine Klasse Contact, in welcher die ganzen Daten (Vorname, Name, Ort, usw.) mit Konstruktor, getter und setter enthalten sind. Zusatzlich eine Arraylist<Contact> in welcher die Objekte vom Typ Contact hinzugefügt werden können. Diese Arraylist möchte ich gerne abspeichern. Mit der genannten Methode wird aber bei jeder Änderung eine neue xml Datei geschrieben, statt immer nur die Änderungen.
 
Kann man das dann trotzdem mit Arraylisten machen, oder muss man dann für alles direkt Datenbankbefehle benutzen?
Ja und ja 😉

Ich habe eine Klasse Contact, in welcher die ganzen Daten (Vorname, Name, Ort, usw.) mit Konstruktor, getter und setter enthalten sind. Zusatzlich eine Arraylist<Contact> in welcher die Objekte vom Typ Contact hinzugefügt werden können.
Das klingt zumindest besser als die Beschreibung aus erstem Beitrag 😉
 
Kennst du gerade eine Seite oder sonst was, wo das ganze einigermassen verständlich erklärt wird? 🙂 Ich habe mal was von Hibernate oder BaseX gelesen. Kannst du da was empfehlen? Am besten etwas, wo ich nicht den ganzen Quelltext des Programms wieder umschreiben muss. 😉
 
Stichwort wäre JDBC

Wenn du das ganze Programm umschreiben muss, um das Speicherformat zu ändern, hast du grundlegend was falsch designed und das umschreiben wäre sowieso ratsam 😉
 
OK, danke euch beiden mal, dann suche ich erstmal nach JDBC und melde mich bei Unklarheiten.
Das mit dem Umschreiben meine ich, da mein Programm immer mit der Arraylist<Contact> arbeitet und Einträge immer in dieser Arrayliste hinzugefügt, gelöscht oder bearbeitet werden und nicht direkt in einer Datenbank. Bei SQL wäre ein Befehl zum Löschen ja sowas in dieser Art:
Code:
DELETE FROM users WHERE username = 'Name'

Bei mir sieht das im Moment so aus:
Java:
private void handleDeletePerson() {
    int selectedIndex = contactTable.getSelectionModel().getSelectedIndex();
    if (selectedIndex >= 0) {
        contactTable.getItems().remove(selectedIndex);
    } else {
        //Nothing selected
        Dialogs.showWarningNoPersonSelected();
    }
    }
 
Schau dir mal etwas über 'java h2 Database' an, da bekommst du sehr gute Beispiele.

Ein Löschen der Tabelle würde bei H2 z. B. so aussehen.

Code:
Statement st = conn.createStatement();

            String delete = "DROP TABLE IF EXISTS " + table;
            st.executeUpdate(delete);

Löschen einer Zeile, würde ich genau sowie du es geschrieben hast machen.

Solltest du deine Arraylist in einem Zug an die Datenbank übergeben(Insert) solltest du dir noch die '.addBatch()' Methode anschauen.
 
Zuletzt bearbeitet:
Besten Dank für die Antwort dcode. Verstehe ich das richtig, dass man in deinem beschriebenen Fall dann zuerst die gesamte Tabelle löschen würde und dann bei einer Änderung wieder die gesamte Arraylist in eine neue Tabelle in der Datenbank schreiben würde?

Falls ja, ist das aber eigentlich ähnlich, wie bei der xml Datei Methode, ausser dass man hier eine Datenbank benutzt und dort einfach nur mit einer xml Datei arbeitet, oder? In beiden Fällen würden dann immer die kompletten Daten neu geschrieben werden.
 
Jetzt mal nicht das Kind mit dem Bade ausschütten. So wie es aussieht musst du die beiden Methoden von da oben umschreiben ...
Ja, wie ich schon sagte...
Solange das vernünftig strukturiert ist, muss man nur das speichern ändern

Besten Dank für die Antwort dcode. Verstehe ich das richtig, dass man in deinem beschriebenen Fall dann zuerst die gesamte Tabelle löschen würde und dann bei einer Änderung wieder die gesamte Arraylist in eine neue Tabelle in der Datenbank schreiben würde?

Falls ja, ist das aber eigentlich ähnlich, wie bei der xml Datei Methode, ausser dass man hier eine Datenbank benutzt und dort einfach nur mit einer xml Datei arbeitet, oder? In beiden Fällen würden dann immer die kompletten Daten neu geschrieben werden.

Das wäre ein (nicht unbedingt optimaler) Weg das zu lösen.

Ratsamer wäre es aber, nur die jeweilige Änderungen zu speichern und nicht alles 😉
Wie gut das Möglich ist, hängt von der Struktur ab. Liegen die gesamten Daten in der Liste?
 
@mrBrown
Ja, die gesamten Daten liegen in einer Liste.
Dann müsste es eine Art Befehl geben, welcher die Arrayliste mit der Datenbank vergleicht und dann nur die gemachten Änderungen vornimmt, oder? Gibt es sowas?
 
Das wird so nicht funktionieren. Sollen denn die Daten am Ende nach Betätigung eines Speichern-Buttons oder so ähnlich gespeichert werden ? Dann könntest du bei den Elementen deiner ArrayList so was wie einen Status mitführen: Neu, gelöscht, geändert, unverändert. Und das wertest du beim speichern aus. Oder du speicherst jeden Eintrag direkt nach der entsprechenden Aktion des Benutzers.
 
Hier mal ein mini Beispiel wie ich das mit einer Database angehen würde.
Natürlich musst du dann noch das löschen usw. mit Einbauen, auch würde ich alles in je einer Methode bauen.

Es ist alles ganz einfach gehalten, du solltest dir natürlich mehr Gedanken machen.

Code:
import java.sql.*;
import java.util.ArrayList;

public class Database {

    public static void main(String[] a) throws Exception {
        Connection conn = null;
        String tab = "TABELLE";
      
        ArrayList arrayList = new ArrayList();
        arrayList.add("Hans Schmidt, Hauptstrasse 5, 12345 Berlin");
        arrayList.add("Peter Müller, Hauptstrasse 80, 12345 Hamburg");

        try {

            conn = DriverManager.getConnection(
                    "jdbc:h2:~/.javabeginners/h2Test", "", "");

            Statement stmt = conn.createStatement();

            // Ueberprueft ob die Tabelle vorhanden ist, sollte dies nicht so sein, wird die Neue erstellt
            String createQ = "CREATE TABLE IF NOT EXISTS "
                    + tab
                    + "(ID INT PRIMARY KEY AUTO_INCREMENT(1,1) NOT NULL, NAME VARCHAR(255))";
            stmt.executeUpdate( createQ);
          
            for ( int i = 0; i < arrayList.size(); i++ ) {
                // legt jede adresse in einer Neuen Zeile an
                String insertQ = "INSERT INTO " + tab
                        + " VALUES(TRANSACTION_ID(), '" + arrayList.get(i) + "')";
                stmt.executeUpdate( insertQ);
            }

            conn.close();
        } catch ( SQLException e) {
            e.printStackTrace();
        } finally {
            if ( conn != null)
                try {
                    conn.close();
                } catch ( SQLException e) {
                    e.printStackTrace();
                }
        }
    }

}
 
Zuletzt bearbeitet:
@mrBrown
Ja, die gesamten Daten liegen in einer Liste.
Dann müsste es eine Art Befehl geben, welcher die Arrayliste mit der Datenbank vergleicht und dann nur die gemachten Änderungen vornimmt, oder? Gibt es sowas?

Du kannst in deiner Datenbank Primary keys vergeben, somit kann der die Adresse nur einmal einfügen.
Ich hab das mal in meinem Beispiel von oben für eine 'id' mit verbaut. Da kann die Id nur einmal in der Tabelle vorkommen.
Allerdings musst du das die darauf folgende Exception abfangen.
 
Zuletzt bearbeitet:
Danke für die Antworten.

Oder du speicherst jeden Eintrag direkt nach der entsprechenden Aktion des Benutzers.
Also mir wäre lieber, wenn es direkt bei einer Änderung gespeichert werden würde. Wenn ich nach der entsprechenden Aktion des Benutzers die Änderung in der Datenbank vornehme, muss ich sie aber auch in der Arrayliste vornehmen, oder? Ich müsste also um einen Eintrag zu löschen den Befehl "contactTable.getItems().remove(selectedIndex);" für die Arrayliste ausführen und danach noch den Befehl um die Änderung in der Datenbank vorzunehmen, oder?

@dcode
Danke für das Beispiel. So ganz schlau werde ich daraus aber noch nicht. 🙂 Wie funktioniert das dann, wenn ich zum Beispiel einen neuen Kontakt hinzufüge? Jetzt wird dieser ja einfach zur Arrayliste mit den bereits bestehenden Kontakten hinzugefügt. Brächte ich dann eine sperate Arrayliste in welcher nur der neue Kontakt enhalten ist und diese wird dann zur Tabelle hinzugefügt? Oder kann ich dann die komplette geänderte Arrayliste durch den von dir genannten Befehl
Java:
            for ( int i = 0; i < arrayList.size(); i++ ) {
                // legt jede adresse in einer Neuen Zeile an
                String insertQ = "INSERT INTO " + tab
                        + " VALUES(TRANSACTION_ID(), '" + arrayList.get(i) + "')";
                stmt.executeUpdate( insertQ);
            }
zur Datenbank hinzufügen und diese fügt auf Grund der IDs nur den neuen Eintrag hinzu? Ist die ID in der Datenbank die gleiche, wie diese der Arrayliste, oder? Also der erste Eintrag der Datenbank hat die ID 0, sowie der Erste Eintrag in der Arrayliste die ID 0 hat?
 
Zuletzt bearbeitet:
Wenn du eh bei jeder Änderung die Änderungen sofort in der DB nachvollziehen willst dann brauchst du halt je eine Methode zum Einfügen, ändern, löschen eines Elementes aus deiner ArrayList und die rufst du da auf wo du auch die Änderung in deiner ArrayList vollziehst. Fertig.
und diese fügt auf Grund der IDs nur den neuen Eintrag hinzu
nein tut sie nicht sondern du kriegst entsprechende Exceptions. Allerdings ist es sehr unschön dieses ganze DB-Handling über Exceptions zu steuern.
 
Danke JStein52 für die Erklärung. Ich bin mir gerade am überlegen, ob sich der ganze Aufwand mit einer Datenbank überhaupt lohnt, bei diesen kleinen Datenmengen. Bei etwa 1000 Einträgen, ist die Dateigrösse etwa 1MB. Ein Eintrag hat also etwa 1kB. Wenn man mal davon ausgeht, dass die meisten vielleicht etwa 200 Einträge (wenn überhaupt) in einem Adressbuch gespeichert haben, hat die xml Datei nur 200kB.
Was denkst du? Spielt es bei so kleinen Datenmengen überhaupt eine Rolle, ob die Datei immer neu generiert wird?
 
Ja, die Daten sind lokal und gehören einem Benutzer. Danke euch allen, für die Hilfe. Ich belasse es jetzt mal bei der xml Methode und beim nächsten Projekt sehe ich mir dann vielleicht mal die Datenbanken an. 🙂
 

Neue Themen


Zurück
Oben