' maskieren in SQL-Statement?

Status
Nicht offen für weitere Antworten.

Verjigorm

Top Contributor
Hallo,

ich lese Daten von einer Datenbank ein und schreibe sie in eine andere.

Beim Einlesen der Daten habe ich auch Kommentare, die ein ' enthalten.
Wenn ich nun damit z.b. ein SQL-Insert bilde, kracht es, weil ich versuche ja dann einen Wert einzufügen, der so aussieht:

Code:
...'Wiesbaden Ost - N'lahnstein'...

Ok, das ' maskieren dachte ich mir, aber es geht weder

Code:
...'Wiesbaden Ost - N\'lahnstein'...
noch

Code:
...'Wiesbaden Ost - N\\'lahnstein'...

(ohne ' gehts durch, es liegt also genau an dieser Stelle)

Steh wohl grad aufm Schlauch *kopfkratz*

EDIT: Und ja, PreparedStatement benutze ich auch

mfg Verjigorm
 

musiKk

Top Contributor
Manche (viele? alle?) Datenbanken maskieren das wohl mit einem doppelten Apostrophen. Für sowas solltest du aber vielleicht ein PreparedStatement mit Platzhalter verwenden, da wird die Maskierung abgenommen.

edit: Nagut, dann vergiss den zweiten Satz.

Nochmal ein Nachtrag: Welche Datenbank wird denn verwendet? Ich habe hier lokal gerade nur Derby aber damit klappt das wunderbar.
 

Verjigorm

Top Contributor
Wird in Access geschrieben

Code:
	public void insert(String sql) 
	{
		PreparedStatement stmt = null;
		try {
			stmt = connection.prepareStatement(sql);
			
			try {
				stmt.execute();
				
			}
			catch (SQLException e) 
			{
				if(e.getMessage().contains("General error"))
				{
					main.logDebug("EquipmentNr bereits vorhanden: " + e.getMessage() + ": " + sql);
				}
				else
				{
					main.logDebug(e.getMessage() + ": " + sql);
					e.printStackTrace();
				}
			}

		} catch (SQLException e) 
		{
			main.logDebug(e.getMessage() + ": " + sql);
			e.printStackTrace();
		}
		finally
		{
			if(stmt != null)
				try {stmt.close(); stmt = null;} catch (SQLException e) {e.printStackTrace();}
		}
	}
 

The_S

Top Contributor
Ein PreparedStatement nützt dir in der Hinsicht gar nichts, wenn die Variablen nicht über die entsprechenden Setter (setString, setInt, ...) setzt.
 

musiKk

Top Contributor
So verwendet man ein PreparedStatement nicht. Da, wo die Daten reinkommen, schreibst du im SQL-Code nur ein Fragezeichen und über die diversen set... (z. B. setString() wäre für dich relevant)-Methoden weist du diesen Platzhaltern dann konkrete Werte zu. Dabei wird die Maskierung übernommen.
 
M

maki

Gast
Jetzt müsstest du uns noch den sql String dazu geben ;)

Abgesehen davon sind geschachtelte try/ctach Blöcke welche beide dieselbe Exception werfen und fangen sinnfrei, was bitte ist den main.logDebug() ??? Wozu einen Stacktrace auf sysout printen wenn man logging verwendet?
Wieso erst durch eine Exception erfahren dass der Datensatz schon angelegt war?

Fragen über fragen...
 

ARadauer

Top Contributor
stimmt poste mal deinen sql string...

es ist wichtig, dass darüber gesprochen wird. die richtige verwendung von prepared statements, ist essentiel für sicherheit (injections) und performance...
 

musiKk

Top Contributor
Nunja. An der direkten Abfolge von
Code:
stmt = connection.prepareStatement(sql);
und
Code:
stmt.execute();
sieht man schon, dass da nix mit Maskierung passieren kann.

Was nicht heißen soll, dass der SQL-String nicht interessant sein könnte. ;)
 

Verjigorm

Top Contributor
Genau deswegen frag ich ja, weil ich nicht weiss, wie es besser geht.

Ausserdem ist der Code weit entfernt vom Status "fertig", das ist die Pre-Alpha-Version ;)

Insert:
Code:
"INSERT INTO B4SKZ VALUES('3507', 'Wiesbaden Ost - N'lahnstein', '36.245', '42.736', '6.49', '312041',
 'Mainz','310623', 'Mainz', '310367', 'Frankfurt/', 'F-', 'Ffm+Saarbr.', '06', 'Hessen', '', '', '01', '01', '01', '01', '03', '02', '08', 
'01', '07', '01', '24', '', '2e', 'D4', 'VG1', '26 V-Netz G  120 kmh', 'KSW4MI', '16M', '16FM', 'FM1', 'KSW4MI Rh.-Main/Spessart',
 '35071036.245')
 

musiKk

Top Contributor
Hammerst. Zunächst mal: Sind das wirklich alles Strings? Weil da sind ja auch viele Zahlen dabei.

Zum Maskieren: Schau mal in der API-Dokumentation bei PreparedStatement, da ist ein kleines Beispiel gegeben. Das läuft dann auf etwas hinaus wie
Code:
stmt = connection.prepareStatement("INSERT INTO B4SZK VALUES(?, ?, ?, ..........
stmt.setString(1, ...
stmt.setString(2, ...
stmt.setString(3, ...
...
Dabei kümmerst du dich nicht um die Apostrophen, das übernimmt der Datenbank-Treiber.
 

ARadauer

Top Contributor
Verjigorm hat gesagt.:
Genau deswegen frag ich ja, weil ich nicht weiss, wie es besser geht.

Ausserdem ist der Code weit entfernt vom Status "fertig", das ist die Pre-Alpha-Version ;)

Insert:
Code:
"INSERT INTO B4SKZ VALUES('3507', 'Wiesbaden Ost - N'lahnstein', '36.245', '42.736', '6.49', '312041',
 'Mainz','310623', 'Mainz', '310367', 'Frankfurt/', 'F-', 'Ffm+Saarbr.', '06', 'Hessen', '', '', '01', '01', '01', '01', '03', '02', '08', 
'01', '07', '01', '24', '', '2e', 'D4', 'VG1', '26 V-Netz G  120 kmh', 'KSW4MI', '16M', '16FM', 'FM1', 'KSW4MI Rh.-Main/Spessart',
 '35071036.245')

ich hoffe nicht, dass dieser string aus eingaben vom benutzer zusammen gebaut wird...
http://de.wikipedia.org/wiki/SQL_Injection
 
M

Michael...

Gast
in Oracle wird einem ' maskiert. Also müsste das so ausschauen:
'Wiesbaden Ost - N''lahnstein'
 

Verjigorm

Top Contributor
Im Endeffekt schon, das sind Inhalte einer anderen datenbank, die halt wohl irgendwann mal von Hand gefüllt wurden.

ARadauer hat gesagt.:
Verjigorm hat gesagt.:
Genau deswegen frag ich ja, weil ich nicht weiss, wie es besser geht.

Ausserdem ist der Code weit entfernt vom Status "fertig", das ist die Pre-Alpha-Version ;)

Insert:
Code:
"INSERT INTO B4SKZ VALUES('3507', 'Wiesbaden Ost - N'lahnstein', '36.245', '42.736', '6.49', '312041',
 'Mainz','310623', 'Mainz', '310367', 'Frankfurt/', 'F-', 'Ffm+Saarbr.', '06', 'Hessen', '', '', '01', '01', '01', '01', '03', '02', '08', 
'01', '07', '01', '24', '', '2e', 'D4', 'VG1', '26 V-Netz G  120 kmh', 'KSW4MI', '16M', '16FM', 'FM1', 'KSW4MI Rh.-Main/Spessart',
 '35071036.245')

ich hoffe nicht, dass dieser string aus eingaben vom benutzer zusammen gebaut wird...
http://de.wikipedia.org/wiki/SQL_Injection
 
M

maki

Gast
>> Im Endeffekt schon, das sind Inhalte einer anderen datenbank, die halt wohl irgendwann mal von Hand gefüllt wurden.

Solange du verstanden hast dass du deine PreparedStatements falsch einsetzt - nämlich so als ob es gar keine sind - und verstanden hast dass du sie richtig einsetzen musst um deren Vorteile zu geniessen, ist alles in Ordnung ;)

@Michael...
Themaverfehlung ;)
 
S

SlaterB

Gast
aber die Hauptmessage der anden Antworten ist ja, dass diese Frage gar nicht zu stellen ist,
sondern dass man das unbedingt dem PreparedStatement überlassen soll
 
M

maki

Gast
>> ??? hab doch nur auf die Frage im Titel geantwortet :-(

Schon klar, haben wir ihm aber (hoffentlich) ausgeredet , da er dieses Problem (und viele andere, siehe SQL Injection) gar nicht hätte wenn er die PreparedStatements richtig nutzen würde.
 

Verjigorm

Top Contributor
also ich hab mir jetzt mal
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
durchgelesen.

Da steht dann
Supplying Values for PreparedStatement Parameters

You need to supply values to be used in place of the question mark placeholders (if there are any) before you can execute a PreparedStatement object.

Placeholders ... da steht schonmal nicht, welche das sind.
Erst im beispiel unten wird klarer:
Es handelt sich wohl um die ? (Fragezeichen)

Nun kann ich aber keine Fragezeichen als Placeholder benutzen, weil die bereits auch im Text benutzt werden.

2. Frage die ich mir stelle:
Ich hab jetzt mal in das obige Beispiel ein Fragezeichen eingefügt,
wenn ich dann
Code:
stmt.setString(1, "ersatz");
mache, kommt ne NullpointerException obwohl stmt != null ist

3. Verstehe ich nicht, wo ich da etwas gewinne?
 
S

SlaterB

Gast
> Nun kann ich aber keine Fragezeichen als Placeholder benutzen, weil die bereits auch im Text benutzt werden.

dieser Satz ist nicht verständlich,
in einem PreparedStatement kann man ? benutzen,
nicht mehr und nicht weniger gilt,

was ist nun deine Frage/ Problem?

--------

> kommt ne NullpointerException obwohl stmt != null ist

tja,
'ich verwende Code und es kommt eine Exception'
da wird jeder eine Antwort wissen ;)

Code posten?..
 

The_S

Top Contributor
1.) Vermutlich werden die ? in deinem PreparedStatement im Text vorkommen oder? Von daher verwendest du in deinem SQL keine Placeholders, sondern nur in den Parametern. Das ist völlig ohne Probleme!

2.) stmt muss aber null sein, sonst kann es bei diesem Code keine NPE geben!

3.) Du gewinnst, indem du dich nicht mehr ums Escapen kümmern musst.
 

Verjigorm

Top Contributor
Ok, das ist imho eins der schlechtesten Tuts, die ich von Sun je gelesen habe, da grade bei INSERTS nicht klar ist, wo das Fragezeichen hinkommt.

ich dachte ich kann sowas machen:
Code:
stmt = con.prepareStatement("INSERT INTO B4SKZ VALUES('3507', 'Wiesbaden Ost - N?lahnstein', '36.245', ....");
stmt.setString(1, "'");

aber es muss so lauten:

Code:
stmt = con.prepareStatement("INSERT INTO B4SKZ VALUES('3507', ?, '36.245', ....");
stmt.setString(1, "Wiesbaden Ost - N'lahnstein");
 

ARadauer

Top Contributor
Code:
   stmt = con.prepareStatement("INSERT INTO B4SKZ VALUES(?,?,?)"); 
   stmt.setString(1, 3507);
   stmt.setString(2 "Wiesbaden Ost - N?lahnstein");
   stmt.setFloat(3, 6.245);
so würde man das machen...

was hast du davon? performance gewinn.. vielleicht merkt mans bei einem nicht, aber bei ein paar updates

zb
Code:
PreparedStatement updateSales;
String updateString = "update COFFEES " +
                      "set SALES = ? where COF_NAME like ?";
updateSales = con.prepareStatement(updateString);
int [] salesForWeek = {175, 150, 60, 155, 90};
String [] coffees = {"Colombian", "French_Roast", "Espresso",
                     "Colombian_Decaf", "French_Roast_Decaf"};
int len = coffees.length;
for(int i = 0; i < len; i++) {
                updateSales.setInt(1, salesForWeek[i]);
                updateSales.setString(2, coffees[i]);
                updateSales.executeUpdate();
        }
wird man merken

und du musst dich nicht ums escapen kümmern, was dir einige probleme erstpart und die ganze sache sicherer macht (sql injections..)
 

Verjigorm

Top Contributor
Also ich bau dann ne "Grundstruktur"

Code:
stmt = con.prepareStatement("INSERT INTO ? VALUES(?, ?, ?, ....., ?)";

und nutze dann für sämtliche Parameter die setXXX-Funktionen?


edit: Danke ARadauer war etwas schneller =)
 
M

maki

Gast
INSERT INTO ?

ist quatsch, den Tabellennamen will man ja nicht über parameter steuern, ist ziemlich sinnfrei...
 
S

SlaterB

Gast
Tabellennamen kann man nicht unbedingt parametrisieren,
sollten aber auch keine Benutzereingabe sein und eh keine Sonderzeichen enthalten

da auch von maki schon geschrieben wenigstens noch was lustiges:
stmt = con.prepareStatement("? ? ? ?(?, ?, ?, ....., ?)";
 

Verjigorm

Top Contributor
wieso nicht?

Ich exportiere ja xx Tabelleninhalte von A nach B, irgendwo muss ich ja den Tabellennamen angeben
 
M

maki

Gast
Naja, die "echten" Parameter sollten per setXXX gesetzt werden.

Ändern sich die Tabellennamen in B?
Wenn ja, musst du deinen SQL String immer noch zusammenbauen, ala

String sql = "INSERT INTO " +tablename+ " VALUES(?, ?, ?, ?)";

Aber wenn du Tabelleninhalte nur kopierst, wozu dann überhaupt Java???
Das geht doch pro Tabelle mit nurr einem SQL Statement...
 

Verjigorm

Top Contributor
maki hat gesagt.:
Ändern sich die Tabellennamen in B?

Ja

maki hat gesagt.:
String sql = "INSERT INTO " +tablename+ " VALUES(?, ?, ?, ?)";

Jo, so siehts jetzt auch aus

maki hat gesagt.:
Aber wenn du Tabelleninhalte nur kopierst, wozu dann überhaupt Java???
Das geht doch pro Tabelle mit nurr einem SQL Statement...

1. Man kann in Access nicht jede Tabellenstruktur übernehmen.
Access is da ziemlich hartnäckig mit inkompatiblen Parametern
Code:
CREATE TABLE test (
  doublewert double(15,3) default NULL
)
geht z.b. nicht, weil Access nur double kennt, kein double(15,3)
d.h. alle Parameter müssen von hand angepasst werden, jedenfalls hab ich noch nix anderes dazu gefunden.
Eigendlich dachte ich, der ODBC-Treiber nimmt mir genau diese Arbeit ab, macht er aber nicht.

2.
Jeder Datensatz durchläuft eine Plausibilitätsprüfung und wird dann entweder als Insert oder Update behandelt oder weggeschmissen ^^

Die alte Superbase-Tabelle kannte z.B. keine Primary/ForeignKeys oder sonstwas und das ändert sich nun.
 
M

maki

Gast
Ach stimmt, dass war ja die "Konvertiere Access DBs mit Java zu anderen DBs" Geschichte... übrigens ist JDBC ziemlich low-level, da lohnt sich der Einsatz von Frameworks, zB. wäre iBatis in so einem Fall ideal imho, aber dafür ist es wohl zu spät.
 

robertpic71

Bekanntes Mitglied
Zum variablen Tabellennamen: Insofern nicht sinnvoll, da mit einer anderen Tabelle wahrscheinlich auch eine andere Feldanzahl=Parameteranzahl=Fragezeichen dahinter erwünscht sind.

Die Vorteile nocheinmal zusammengefasst:
* Schutz vor SQL-Injection
* kein Escapen
* kein Komma oder Punkt Problem bei numerischen Werten (Spracheinstellungen Server vs. Client)
* kein Datumsformatproblem (jjjj-mm-tt oder tt... wieder Spracheinstellungen), einfach Date als Parameter
* Performancevorteil (es muss nur der Befehl geparst werden nicht die Parameter, u.U. Wiederverwendung vom Statement)

Nachteile:
Bei vielen Feldern wird die Fragezeichenflut etwas unübersichtlich. Wenn man kein Persitenzframework nehmen will oder kann (Access? ODBC-Bridge?), bieteten sich z.B. die Spring-JDBC-Templates an, welche NamedParameters unterstützen.

also z.B.

Code:
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id" ..
..
Map<String, Object> parameters = new HashMap<String, Object>(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());

/Robert
 

Verjigorm

Top Contributor
Noch ne Frage:

Kann ich mir den endgültigen Query-String irgendwie ausgeben lassen?
Also nachdem alle setXXX-Methoden ausgeführt wurden?
Finde da irgendwie keine Funktion für
 
S

SlaterB

Gast
an so einer Stelle ist es immer wieder schön, dass man ja eine Programmiersprache bei der Hand hat,

blitzschnell ist da PreparedStatement erstetzt durch MyBetterStatement,
welches QueryString + Parameter annimmt und sowohl ein PreparedStatement zusammenbaut als auch einen String fürs Logging

da kann man dann auch gleich wieder einen Konstruktor oder eine Factory-Methode
Code:
createInsertStatement(tabellenname,"3507", stadtName, ..);
basteln und muss sich überhaupt nicht mehr um ? usw kümmern,
bei verschiedenen Parametertypen (nicht nur String) wirds generisch etwas unschön, aber machbar

Vorteil bei sowas ist auch, dass man eine abhängige Sache wie PreparedStatement mit seinen ? nicht im ganzen Programm verteilt,
wechselt man zu einer anderen Library wie Hibernate, kann man dann die entscheidenen Teile komfortabel an nur einer Stelle umbauen
 

Verjigorm

Top Contributor
würde es eine vorgefertigte Methode geben, die einem das komplette Statement zurückgibt, wär alles ok.

Wieso gibts das nicht?
das ist doch absolutes must-have, zu sehen was fürn Query ausgeführt wird
 
S

SlaterB

Gast
exakt kann man das vielleicht gar nicht machen, weil der Parameter einzeln an die DB übergeben und erst dort maskiert wird usw (Oracle '', MySQL \'),

allzuviel Verständnis dafür habe ich aber auch nicht,
zumindest eine Vorschau mit direkt eingesetzen Parametern in der unescapten Java-Schreibweise wäre leicht möglich
(so wie man es in der eigenen Logging-Klasse dann machen wird)


ist aber Standard, selbst bei Hibernate QueryLog werden Parameter nicht eingesetzt
(edit: evtl. gibts dafür doch ne Konfigurationsmöglichkeit, erinnere mich schwach an soetwas in der Richtung, für JDBC aber uninteressant)
 
M

maki

Gast
Verjigorm hat gesagt.:
würde es eine vorgefertigte Methode geben, die einem das komplette Statement zurückgibt, wär alles ok.

Wieso gibts das nicht?
das ist doch absolutes must-have, zu sehen was fürn Query ausgeführt wird
Hast du meinen Post gelsen?
 
M

maki

Gast
Dann mach es doch richtig ;)

"Logging mit Properties-Konfigurationsdatei" steht da auch drinn, bin kein Fan von der Seite.... echt dämlich Leuten ernsthaft vorzuschlagen log4j mit einer Klasse anstatt der Datei zu konfigurieren.
 

Verjigorm

Top Contributor
Das ich für dieses "Simple-Logging" keine lib brauch, sondern es mit nem normalen Filewriter auch machen könnte, hab ich schon gemerkt ;)
 
M

maki

Gast
Verjigorm hat gesagt.:
Das ich für dieses "Simple-Logging" keine lib brauch, sondern es mit nem normalen Filewriter auch machen könnte, hab ich schon gemerkt ;)
Ein FileWriter ist kein echtes Logging, nur eine andere Version von System.out

Von allen Logging Frameworks bietet log4j am meisten, zumindest wenn man es mit der Java Logging API und dem commons-logging vergleicht.
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben