Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Programmierung unter AOO/LO (StarBasic, Python, Java, ...)

Moderator: Moderatoren

marcel_at_work
****
Beiträge: 195
Registriert: Sa, 24.04.2010 15:51
Wohnort: Basel [CH]

Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Beitrag von marcel_at_work »

Ein frohes, erfolgreiches neues Jahr an alle, 8)

ich möchte meine bisherigen Datenbankverbindungen ein wenig umstrukturieren. Warum aber generiert der folgende Zugriff auf eine externe Datenbank per com.sun.star.sdbc.DriverManager, bei Aufruf der Methode .findColumn, einen Fehler?

Code: Alles auswählen

Public Function setQueryByDriver(oQuery As Object, sCommand As String) As Boolean
	Dim oDriverManager As Object
	Dim oConnection As Object
	Dim sSDBC_URL As String
	Dim oStatement As Object
	Dim arrArgs(1) As New com.sun.star.beans.PropertyValue

	sSDBC_URL = "sdbc:odbc:mySQLiteDB"

	oDriverManager = CreateUnoService("com.sun.star.sdbc.DriverManager")
	oDriverManager.setLoginTimeout(5)

'	arrArgs(0).Name = "user"
'	arrArgs(0).Value = ""
'	arrArgs(1).Name = "password"
'	arrArgs(1).Value = ""

	oConnection = oDriverManager.getConnectionWithInfo(sSDBC_URL, arrArgs())

	If oConnection.isClosed() Then Goto NOCONNECTION

	oStatement = oConnection.createStatement()

	oQuery = oStatement.executeQuery(sCommand)

	setQueryByDriver() = True
Exit Function

NOCONNECTION:
	setQueryByDriver() = False
End Function

Code: Alles auswählen

call setQueryByDriver(oQueryConfig, "SELECT * FROM SYSTEM_CONFIG")
	
With oQueryConfig
	While .Next()
		Select Case .getString(.findColumn("property"))
		Case "cameraDirectory"	: sCAM_DIR	= .getString(.findColumn("value"))

		Case Else
	
		End Select
	Wend
End With
Message: unsupported column attribute 12.

Laut Xray unterstützt das Datenobjekt die Methode .findColumn. Hat irgendjemand vielleicht eine Idee dazu?

Viele Grüße,

Marcel
[Win 10 Pro x64/Downgrade 7, AOO 4.1.6 und LO 6.3.0.4]
Toxitom
********
Beiträge: 3768
Registriert: Di, 12.08.2003 18:07
Wohnort: Wiesbaden
Kontaktdaten:

Re: Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Beitrag von Toxitom »

Hey Marcel,

Hab jetzt keine fertige Lösung für Dich, und habe es auch nicht gegengetestet. Aber ein paar Denkanstöße:

Ich habe schon selbst mit der Methode gearbeitet - und sie funktioniert. Aber:

- Deine Verbindung und in Folge die SQL Abfrage liefert Dir ein Result-Set Objekt - dieses unterstützt die Methode findColumn().
- wie du das in Deinem Code tatsächlich realisierst, ist mir so nicht ganz klar.
Einfacher wäre es doch so:

Code: Alles auswählen

Function setQueryByDriver(sCommand As String) 
  ' dein Code wie gehabt
  oResultSet = oStatement.executeQuery(sCommand)
  setQueryByDriver = oResultSet
end function

function ????
  dim oResultset as Object
    oResultSet = setQueryByDriver( "SELECT * FROM SYSTEM_CONFIG")
    'hier müsste nun ne Abfrage kommen, ob überhaupt Daten vorhanden sind. Die Abfrage hängt allerdings vom verwendetetn DB-System ab.
    With oResultSet
    ...
    end with 
    
end function

Wahrscheinlich aber musst Du die SpaltenNummern vor der .next() Methode abfragen und in Variablen zwischenspeichern.
Über die next() Methode wird Dir ja der nächste Datensatz zurückgeliefert - und ob der die findColumn Methode unterstützt?? bin ich mir nicht sicher.

Noch ein Tip:

Eine Abfrage "Select * from xxx" um später die zwei benötigten Spalten zu identifizieren ist nicht wirklich geschickt.
Hier wäre sicher die Abfrage "SELECT property, value FROM SYSTEM_CONFIG" sicher deutlich effektiver und sinnvoller.

VG
Tom
Unterstützer LibreOffice, zertifizierter Trainer und Berater
Bücher: LibreOffice 6- Einstieg und Umstieg
Makros Grundlagen - LibreOffice / OpenOffice Basic
marcel_at_work
****
Beiträge: 195
Registriert: Sa, 24.04.2010 15:51
Wohnort: Basel [CH]

Re: Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Beitrag von marcel_at_work »

Hallo Tom,

danke für die schnelle Antwort.
Eine Abfrage "Select * from xxx" um später die zwei benötigten Spalten zu identifizieren ist nicht wirklich geschickt.
Hier wäre sicher die Abfrage "SELECT property, value FROM SYSTEM_CONFIG" sicher deutlich effektiver und sinnvoller.
Da stimme ich dir absolut zu. SELECT erstelle ich fast ausschließlich mit den benötigten Spalten. Ich hatte es nur zu Testzwecken kurz abgeändert.

Ich habe mir früher schon oft Gedanken gemacht, ob es sinnvoll ist, das ResultSet von der aufgerufenen Funktion als Parameter zurückzugeben, bin aber bis heute noch zu keinem endgültigen Entschluss gekommen.
Begutachte doch mal unsere beiden Lösungen und berichte mir...

Code: Alles auswählen

'... deine potenzielle Lösung
Dim oResultset As Object
oResultSet = setQuery("SELECT * FROM SYSTEM_CONFIG")
oResultSet.Next

Code: Alles auswählen

'... meine bisherige Lösung
Dim oResultset As Object
setQuery(oResultSet, "SELECT * FROM SYSTEM_CONFIG")
oResultSet.Next
Wenn du einen grösseren Unterschied siehst, als daß ich mit meiner Lösung ein Zeichen einspare, sende ich dir 10 CHF mit der Post. 8)
Wahrscheinlich aber musst Du die SpaltenNummern vor der .next() Methode abfragen und in Variablen zwischenspeichern.
Wie meinst du das genau?

Laut Xray hat das ResultSet von DriverManager (SQLite) keine Column-Eigenschaft, aber die Methode .findColumn. Ich nehme langsam an, das ist ein Fehler in Xray bzw. im DB-Treiber.

Ich benötige die .findColumn-Methode in fast jeder meiner ResultSet-Abfragen. Ich kann damit Datenbanken jederzeit um beliebige Spalten erweitern und Tabellen verschieben, ohne vorhandenen Code nachträglich verändern zu müssen. Zumal man im Code auch direkt die Spaltenzuweisung erkennen kann und nicht erst noch den Index mit der Tabelle abgleichen muss.

Ohne diese Methode ist alles doof! :shock:

Was ist denn dein bevorzugtes Mittel für Verbindungen zu ext. Datenbanken?

Liebe Grüße,

Marcel
[Win 10 Pro x64/Downgrade 7, AOO 4.1.6 und LO 6.3.0.4]
Toxitom
********
Beiträge: 3768
Registriert: Di, 12.08.2003 18:07
Wohnort: Wiesbaden
Kontaktdaten:

Re: Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Beitrag von Toxitom »

Hey Marcel,
Wenn du einen grösseren Unterschied siehst, als daß ich mit meiner Lösung ein Zeichen einspare, sende ich dir 10 CHF mit der Post.
Och, die 10 Franken nehme ich gerne *hihi*. Und ja, es gibt Unterschiede - die sich insbesondere im lesbareren Code und der Klarheit wiederspiegeln. Faktisch funktionieren beide und liefern beide gleiche Ergebnisse. Jeder wie er mag.

In Deinem Fall: "setQuery(oResultSet, "SELECT * FROM SYSTEM_CONFIG")" übergibst Du ein leeres Objekt, das dann seinen Inhalt in der Unterfunktion zugewiesen bekommt. Da die Variable innerhalb der ersten Funktion erzeugt wurde und die Sichtbarkeit auch nur dort vorhanden ist, funktioniert das auch nur, weil im Normalfall die Werte "per Referenz" übergeben werden und somit sich dynamisch ändern.
Sehr schwer zu lesen und kaum nachzuvollziehen - aber eben funktionstüchtig.
Würde Deine "setQuery()" Funktion aber sauber definiert werden " sub setQuery(byVal oResultset as object, byVal sSQL as string)" - so würde Dein Weg nicht mehr funktionieren - byVal übergibt die Variablen als Werte, nicht als Referenz. Aber da wir typischerweise das "byVal" weglassen, werden sie eben als Referenz übergeben und somit auch dynamisch verändert.

Insofern finde ich den Weg über die Rückgabefunktion einer Funktion sinnvoller und leichter zu lesen / nachzuvollziehen. Aber wie gesagt, Geschmackssache.
ch benötige die .findColumn-Methode in fast jeder meiner ResultSet-Abfragen. Ich kann damit Datenbanken jederzeit um beliebige Spalten erweitern und Tabellen verschieben, ohne vorhandenen Code nachträglich verändern zu müssen. Zumal man im Code auch direkt die Spaltenzuweisung erkennen kann und nicht erst noch den Index mit der Tabelle abgleichen muss.
Das aber kann ich nicht nachvollziehen. Habe ich eine Abfrage wie "SELECT SpalteA, SpalteB FROM MeineTab" spielt es überhauüpt keine Rolle , weiviel Spalten die Tabelle tatsächlich hat oder wo die Spalten sich befinden - es wird immer nur die beiden Spalten mit dem Namen "SpalteA" und "SpalteB" in genau der angegebenen Reihenfolge im Resultset zu finden sein. Da kannst Du der Tabelle beliebige Spalten an beliebiger Stelle hinzufügen. Da ändert sich nix. Und damit kann ich die Werte leicht auslesen mit .getString(1) für die 1. Spalte und so weiter.

Je einfacher und klarer der Select Befehl ist, um so klarer ist doch auch der Code.

Und sollte das wirklich mal nicht gehen und Du brauchst SpalteA und SpalteB und rufst komplett alle Spalten ab, dann besorge Dir die Columnnummern eben vorher:

Code: Alles auswählen

nSP1 = oQueryConfig.findColumn("SpalteA")
nSP2 = oQueryConfig.findColumn("SpalteB")

With oQueryConfig
	While .Next()
		Select Case .getString(nSP1)
		Case "cameraDirectory"	: sCAM_DIR	= .getString(nSP2)

		Case Else
	
		End Select
	Wend
End With
(nicht wirklich praktisch getestet.. aber so sollte es gehen)

VG
Tom
Unterstützer LibreOffice, zertifizierter Trainer und Berater
Bücher: LibreOffice 6- Einstieg und Umstieg
Makros Grundlagen - LibreOffice / OpenOffice Basic
marcel_at_work
****
Beiträge: 195
Registriert: Sa, 24.04.2010 15:51
Wohnort: Basel [CH]

Re: Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Beitrag von marcel_at_work »

Hallöchen Tom,

herzlichen Dank! 8)
Würde Deine "setQuery()" Funktion aber sauber definiert werden " sub setQuery(byVal oResultset as object, byVal sSQL as string)" - so würde Dein Weg nicht mehr funktionieren - byVal übergibt die Variablen als Werte, nicht als Referenz. Aber da wir typischerweise das "byVal" weglassen, werden sie eben als Referenz übergeben und somit auch dynamisch verändert.
Die Funktion von ByVal ist mir sehr wohl bewusst. Ich benutzte sie bisher aber grundsätzlich nicht, da ich nicht wenige meiner wiederverwendbaren Funktionen eben genau mit der vorher beschriebenen Syntax erstellt habe.
Ich muss dir zustimmen. Ich denke, allein unter dem Aspekt der Gewissheit (oder auch Logik), daß der zu berechnende Wert bei Aufruf der Funktion "oResultSet = setQuery("SELECT...")" zu jeder Zeit schon in ihr enthalten ist, werde ich das jetzt alles mal abändern. :lol:
Och, die 10 Franken nehme ich gerne *hihi*.
Wenn du willst, gib' mir in irgendeiner Art und Weise eine Adresse oder Bankverbindung - ich stehe zu meinen Aussagen. 8)
Das aber kann ich nicht nachvollziehen. Habe ich eine Abfrage wie "SELECT SpalteA, SpalteB FROM MeineTab" spielt es überhauüpt keine Rolle , weiviel Spalten die Tabelle tatsächlich hat oder wo die Spalten sich befinden - es wird immer nur die beiden Spalten mit dem Namen "SpalteA" und "SpalteB" in genau der angegebenen Reihenfolge im Resultset zu finden sein. Da kannst Du der Tabelle beliebige Spalten an beliebiger Stelle hinzufügen. Da ändert sich nix. Und damit kann ich die Werte leicht auslesen mit .getString(1) für die 1. Spalte und so weiter.
Ich habe das wohl nicht eindeutig genug erklärt. Im Beitrag zuvor schrieb ich etwas von ResultSet-Abfrage, also der Weiterbearbeitung des generierten ResultSets. Du meintest jetzt aber die SQL-Anweisung und im SELECT-Befehl ist natürlich alles absolut eindeutig - da bin ich voll und ganz bei dir!

Hier mal ein Beispiel für eine meiner ResultSet-Abfragen. Viele meiner Dialoge haben zwanzig und mehr I/O-Elemente, welche alle irgendwie mit einer Datenbank verknüpft sind.
Die Spalten-Indizes in den ResultSets sind ja stets die gleichen, egal in welche Richtung die Werte später verschoben werden.

Dies ist ein Auszug einer ResultSet-Abfrage aus einem fertigen Programm, mit der Methode .findColumn (das Objekt .columns davor ist dem verwendeten DB-Treiber geschuldet); Funktion: Erstellung von Zeilen-Arrays für ein TableGrid:

Code: Alles auswählen

	With oQueryOverview
		If Not isNull(oQueryOverview) Then
			While .Next()
				Redim Preserve arrRows(n)
				arrRows(n) = Array(_
					Format(.getInt(.columns.findColumn("ID")), "000000"),_
					.getString	  (.columns.findColumn("realEstate")),_
					.getString	  (.columns.findColumn("building")) & IIF(.getString(.columns.findColumn("floorLevel")) <> "", "→", "") &_
						.getString(.columns.findColumn("floorLevel")) & IIF(.getString(.columns.findColumn("place")) <> "", "→", "") &_
							.getString(.columns.findColumn("place")),_
					.getString	  (.columns.findColumn("concerning")),_
					.getInt		  (.columns.findColumn("priorityID")),_
					.getString	  (.columns.findColumn("processingState")) & IIF(.getString(.columns.findColumn("dateSendNAV")) <> "", " [NAV]", ""),_
					.getInt		  (.columns.findColumn("processed")),_
					.getString	  (.columns.findColumn("dateChange")))
				n = n + 1
			Wend
		End If
		.Close()
	End With
Dieses Programm läuft wunderbar, aber ich benutze hier eben auch noch eine andere Datenbankverbindung.

... und nun mal abgeändert ohne .findColumn:

Code: Alles auswählen

	With oQueryOverview
		If Not isNull(oQueryOverview) Then
			While .Next()
				Redim Preserve arrRows(n)
				arrRows(n) = Array(_
					Format(.getInt(1), "000000"),_
					.getString	  (2),_
					.getString	  (3) & IIF(.getString(4) <> "", "→", "") &_
						.getString(4) & IIF(.getString(5) <> "", "→", "") &_
							.getString(5),_
					.getString	  (6),_
					.getInt		  (7),_
					.getString	  (8) & IIF(.getString(9) <> "", " [NAV]", ""),_
					.getInt		  (10),_
					.getString	  (11))
				n = n + 1
			Wend
		End If
		.Close()
	End With
Wenn nun im letzten Fall, in der Datenbank, rechts neben der ID(1), eine einzige Spalte hinzugefügt wird und sich somit alle anderen Spalten-Indizes um 1 erhöhen, müssen diese dann folglich auch im Code wieder angepasst werden - jeder einzelne Index, außer die ID(1). Und dies in allen korrelierenden ResultSet-Anweisungen. Bei drei solcher Anweisungen (z.B. je einmal QUERY, INSERT, UPDATE) pro Tabelle, sind das dann gleich 60 Werte (bei 20 Feldern) - nur wegen der Änderung einer Spalte in der Datenbank.

Denke ich vielleicht zu umständlich? :shock:

Code: Alles auswählen

nSP1 = oQueryConfig.findColumn("SpalteA")
nSP2 = oQueryConfig.findColumn("SpalteB")

With oQueryConfig
	While .Next()
		Select Case .getString(nSP1)
		Case "cameraDirectory"	: sCAM_DIR	= .getString(nSP2)
Dies macht zumindest die ResultSet-Abfrage übersichtlicher.
Aber hast du noch in Erinnerung, daß die Methode .findColumn per DriverManager nicht unterstützt wird?

WAS mach' ich denn nun genau, um dieses Problem zu lösen? Ich wollte davon weg, die SQLite-Dateien (also externe Datenbanken) erst in OOo-Base anzumelden und erst später über das Dokument darauf zuzugreifen.

Wie macht denn sowas ein Profi, wie du? 8)

Liebe Grüße,

Marcel
[Win 10 Pro x64/Downgrade 7, AOO 4.1.6 und LO 6.3.0.4]
marcel_at_work
****
Beiträge: 195
Registriert: Sa, 24.04.2010 15:51
Wohnort: Basel [CH]

Re: Problem mit der Methode .findColumn bei Datenbank-Anbindung über DriverManager

Beitrag von marcel_at_work »

Hey Tom,

hast du mich etwa vergessen? *heul/schnief* :lol:

Liebe Grüße,

Marcel
[Win 10 Pro x64/Downgrade 7, AOO 4.1.6 und LO 6.3.0.4]
Antworten