Effektives Speichern von Datenbank-Objekten

Hallo, liebe Foren-Gemeindschaft!

Ich sitze momentan an einem Plugin, welches einige Daten aus einer MySQL-Datenbank (per phpMyAdmin) benötigt. Doch (leider?) muss dieses Plugin eine API-Funktion beinhalten, welche jegliche Spalten + deren Werte effektiv speichert, um darauf zugreifen zu können.

Ich habe das momentan per HashMap<String, Object> als Rückgabewert geregelt, doch sobald eine Spalte mehrere Werte enthält (Bsp: ‚id‘ - {2, 4, 143, 4}) wird immer der zuletzt gelesene Wert dem String „Spaltenname“ zugewiesen.
Ich bin mir auch bewusst, dass meine momentane Methode nichts anderes als das oben beschriebene macht, doch deshalb frage ich ja auch hier nach, weil ich mit meinem Wissen am Ende bin.
Meine MySQL-/Java-Erfahrung in der Datenbankprogrammierung ist auch nicht all zu groß :wink:

Momentaner Code:

		
		PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table);
		ResultSet resultSet = statement.executeQuery();
		
		int columnCount = resultSet.getMetaData().getColumnCount();
		HashMap<String, Object> results = new HashMap<>();
		
		while (resultSet.next()) {
			
			for (int i = 0; i < columnCount + 1; i++) {				
				if (i == 0) continue;
				results.put(resultSet.getMetaData().getColumnName(i), resultSet.getObject(i));
			}
			
		}
		
		return results;
	}```


Ich hoffe auf baldige Hilfe :)

Grüße,
Panjab.

Moin,

Du musst zu jedem Key eine Liste speichern, nicht nur ein Objekt. Der rest geht ganz grob in etwa so:

[ul]
[li]testen ob der Key schon vorhanden ist
[/li][li]Ja, vorhanden
[/li][list]
[li]Wert der Liste hinzufügen
[/li][/ul]
[li]Nein, NICHT vorhanden
[/li][ul]
[li]Key anlegen
[/li][li]Wert der Liste hinzufügen
[/li][/ul]
[/list]

hand, mgoel

@mogel

Danke für die Antwort!

Sollte der Rückggabewert dann in etwa so aussehen:

HashMap<String, List<Object>> results = new HashMap<>();

Und wenn ja, wie genau greife ich denn auf die Liste zu, bzw. wie fülle ich sie? Soweit ich weiß muss man mehrdimensionale Listen/Maps aufteilen?

HashMap<String, List<Object>> results = new HashMap<>();
sieht soweit schon mal richtig aus

wenn Du jetzt einen key hast, schaust Du als erstes in Deiner Map ob der key vorhanden ist (contains oder so). Wenn der key nicht vorhanden ist, dann erzeugst Du eine leere List und fügst diese der Map hinzu (glaube put(key, liste)). Ist er vorhanden, dann holst Du Dir die Liste (glaube get). Anschließend fügst Du einfach nur den neuen Wert der Liste hinzu (mit add)

hand, mogel

Nee, HashMap<String, List<Object>> ist Käse. Das geht zwar, aber die Abstraktion ist für einen Nutzer sehr überraschend. Bei allen anderen APIs wird erst Zeilenweise vorgegangen und dann die Column geholt. Das drehst du jetzt um. Damit verwirrst Du alle, inkl dir selbst. Denn in Deinem Codeentwurf versuchst sogar Du, Deine results anders herum zu füllen.

Also, Du willst von einem columnName auf einen Wert mappen. Deine Map enthält also für eine ZEILE des ResultSets alle Column-Names mit ihren zugehörigen Werten. Ein ResultSet hat 0 bis n Zeilen. D.h. Du hast am Ende 0 bis n Maps. DIESE speicherst Du in einer Liste. Was du brauchst ist also List<Map<String,Object>>
Das wäre auch der korrekte Rückgabetyp für Deine Methode.

Danke erstmal für die Beiträge, @mogel und @nillehammer

Allerdings kann ich mir bei dem Beispiel

List<Map<String, Object>>

nicht vorstellen, wie ich mit der Map aus der Liste arbeiten soll. Da wäre es schon einfacher, die Methode von @mogel zu nutzen. :slight_smile:

[…]nicht vorstellen, wie ich mit der Map aus der Liste arbeiten soll. Da wäre es schon einfacher, die Methode von @mogel zu nutzen.

Vom Coding her sind beide Methoden gleich leicht/schwer. Ich finde bei Deinem Ansatz nur verwirrend, dass ich mir erst per column-Name ALLE Werte der Column hole und dann erst per Zeilennummer den Wert der in der entspr. Zeile steht. Das finde ich deswegen komisch, weil es eben genau anders herum ist, als beim ResultSet. Dort holt man sich erst eine Zeile und greift dann auf den Wert per column-Name/index zu. Und wie gesagt, Du selbst hast es in Deiner verschachtelten while-for-Schleife auch anders herum versucht. Hast Dich also selbst verwirrt und ich dachte, diese Verwirrung würde am besten durch meinen Ansatz gelöst…

wenn die Zeilenzuordnung zwischen den einzelnen Keys bleiben soll bzw. muss, dann ist die Version von @nillehammer definitiv zu bevorzugen (afaik hat List keine Index-Garantie)

Tut mir leid, wenn ich mich jetzt ein wenig doof anstelle, aber ich bin doch tatsächlich ein wenig verwirrt. Wie meinst du/ihr das denn nun genau, also das ich mein ResultSet umdrehe?

Bitte nicht hauen, die ersten Anläufe sind die schwersten :slight_smile:

also…

bei meinem Beispiel hast Du pro Key alle Ergebnisse in eienr Liste. Diese Liste ist (AFAIK) nicht Index sicher. D.h. Für “key1” kanns Du in der Liste [ “result1”, “result2”, “result3” ] zurück bekommen. Für “key2” aber durchaus [ “result2”, “result1”, “result3” ]. Sprich, Du hast nicht mehr die Zuordnung wie sie Dir die Datenbank (also das ResultSet) zurück liefert. Wenn Du damit leben kannst und für weitere Dinge nicht wichtig ist, dann kannst Du die Variante nehmen.

Mit der Variante von nillehammer bildest Du das ResultSet exakt ab. Du hast eine Liste, welche die einzelnen Zeilen darstellt. Und in jeder Zeile eine Map mit “key” -> “result”.

@mogel

Aaah, ok, danke! Jetzt habe ich es verstanden! Jetzt bleibt nur noch die Frage offen, wie ich denn nun mit der Map in der Liste arbeite, bzw. wie ich an diese ran komme und deren Keys/Values nutze :frowning:

Am besten erklärt man das anhand eines konkreten Beispiels. Denke Dir eine DB-Tabelle mit zwei Spalten id und text.

PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + table);
ResultSet resultSet = statement.executeQuery();
ResultSetMetaData metaData =  resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// Hier iterierien wir über Zeilen
while (resultSet.next()) {
  // Je Zeile eine neue Map zur Aufnahme der Werte
  // Wir benutzen LinkedHashMap, damit die Reihenfolge des Einfügens
  // der Mappings von Spaltenname->Wert erhalten bleibt. Weil wir sogar schon wissen,
  // Wie viele Mappings enthalten sein werden, geben wir das dem Konstruktor als Argument mit.
  Map<String,Object> aktuelleZeile = new LinkedHashMap<>(columnCount);
  // Innerhalb einer Zeile iterieren wir hier über die einzelnen Spalten
  // Schleife geht bei 1 los, weil entgegen sämtlicher Praxis in der Informatik
  // das doove ResultSet seinen Index bei 1 beginnen lässt.
  // (So macht mans richtig, nicht bei 0 loszählen und continue!)
  for (int i = 1; i <= columnCount; i++) {	
   aktuelleZeile.put(metaData.getColumnName(i), resultSet.getObject(i));
  }
  // Werte sind gefüllt, jetzt zur Liste hinzufügen
  zeilen.add(aktuelleZeile);
}

/*
 * Und hier einige Verwendungsszenarien
*/
// Welche Wertnamen gibt es überhaupt? Gib sie auf Console aus
System.out.println(zeile5.keySet());

// Hole die 5. Zeile aus der Liste (Merke: Listen-Index geht bei 0 los!)
Map<String,Object> zeile5 = zeilen.get(4);
// id und text auf Console ausgeben:
System.out.println("id=" + zeile5.get("id") + ", text=" + zeile5.get("text"));

@nillehammer

Wow, dankeschön! Jetzt ergibt sich mir auch einen Sinn dahinter :slight_smile: Allerdings ist es wohl nicht einfach, ohne Umstände an die Werte zu kommen wenn man beispielsweise die Zeilenzahl garnicht kennt, richtig?

Möchte mich auch nochmal für mein fehlendes Verständnis „entschuldigen“ :wink:

Richtig. Eine Liste funktioniert eben mit Index (“Zeilennummer”). Iteration geht auch. Wenn Du anhand von gegebenen Werten suchen willst, dann programmiere Dir entweder eine Hilfsmethode, der Du die Suchkriterien übergibst, die dann die komplette Liste durchgeht und alle Zeilen zurück gibt, die die gewünschten Werte enthalten. Oder benutze statt eine Liste eine Map, die bspw. den Primary Key auf die Zeilen mappt. Da Du aber eh schon eine DB hast, mach es am besten mit geeigneten SQL-Statements.