Die vorbereitete Anweisung ist eine etwas leistungsfähigere Version einer Anweisung und sollte immer mindestens so schnell und einfach zu handhaben sein wie eine Anweisung.
Die vorbereitete Anweisung kann parametrisiert werden
Die meisten relationalen Datenbanken verarbeiten eine JDBC/SQL-Abfrage in vier Schritten:
Eine Anweisung durchläuft für jede an die Datenbank gesendete SQL-Abfrage immer die vier oben genannten Schritte. Eine vorbereitete Anweisung führt die Schritte (1) - (3) im obigen Ausführungsprozess vorab aus. Daher wird beim Erstellen einer vorbereiteten Anweisung sofort eine Voroptimierung durchgeführt. Der Effekt besteht darin, die Belastung des Datenbankmoduls zur Ausführungszeit zu verringern.
Meine Frage lautet nun: "Ist die Verwendung von Prepared Statement ein weiterer Vorteil?"
Vorteile eines PreparedStatement
:
Das Vorkompilieren und Zwischenspeichern der SQL-Anweisung auf der DB-Seite führt zu einer insgesamt schnelleren Ausführung und der Möglichkeit, dieselbe SQL-Anweisung in Batches wiederzuverwenden.
Automatische Verhinderung von SQL-InjectionAttacken durch eingebautes Entweichen von Anführungszeichen und anderen Sonderzeichen. Beachten Sie, dass Sie hierfür eine der Methoden PreparedStatement
setXxx()
verwenden müssen, um die Werte festzulegen
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
preparedStatement.setString(1, person.getName());
preparedStatement.setString(2, person.getEmail());
preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
preparedStatement.setBinaryStream(4, person.getPhoto());
preparedStatement.executeUpdate();
und daher nicht die Werte in der SQL-Zeichenfolge durch String-Verkettung inline.
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
preparedStatement.executeUpdate();
Erleichtert das Festlegen nicht standardmäßiger Java Objekte in einer SQL-Zeichenfolge, z. B. Date
, Time
, Timestamp
, BigDecimal
, InputStream
( Blob
) und Reader
( Clob
). Bei den meisten dieser Typen ist dies nicht "einfach" möglich a toString()
, wie Sie es in einem einfachen Statement
tun würden. Sie könnten es sogar so umgestalten, dass Sie PreparedStatement#setObject()
in einer Schleife verwenden, wie in gezeigt die Utility-Methode unten:
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
Welches kann wie folgt verwendet werden:
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
preparedStatement.executeUpdate();
Sie sind (einmalig) vorkompiliert, daher schneller für die wiederholte Ausführung von dynamischem SQL (wo sich Parameter ändern).
Durch das Zwischenspeichern von Datenbankanweisungen wird die Leistung der DB-Ausführung erhöht
Datenbanken speichern Caches von Ausführungsplänen für zuvor ausgeführte Anweisungen. Auf diese Weise kann das Datenbankmodul die Pläne für Anweisungen wiederverwenden, die zuvor ausgeführt wurden. Da PreparedStatement Parameter verwendet, kann die Datenbank den vorherigen Zugriffsplan bei jeder Ausführung als dieselbe SQL wiederverwenden, wodurch die Verarbeitung verringert wird. Anweisungen "inline" die Parameter in die SQL-Zeichenfolge und werden daher nicht als die gleiche SQL für die DB angezeigt, wodurch die Cache-Nutzung verhindert wird.
Binäres Kommunikationsprotokoll bedeutet weniger Bandbreite und schnellere Kommunikationsaufrufe zum DB-Server
Vorbereitete Anweisungen werden normalerweise über ein Nicht-SQL-Binärprotokoll ausgeführt. Dies bedeutet, dass die Pakete weniger Daten enthalten, sodass die Kommunikation mit dem Server schneller ist. Als Faustregel gilt, dass Netzwerkoperationen um eine Größenordnung schneller sind als Festplattenoperationen, die um eine Größenordnung schneller sind als CPU-Operationen im Speicher. Daher wirkt sich eine Reduzierung der über das Netzwerk gesendeten Datenmenge positiv auf die Gesamtleistung aus.
Sie schützen vor SQL-Injection, indem sie Text für alle bereitgestellten Parameterwerte maskieren.
Sie bieten eine stärkere Trennung zwischen dem Abfragecode und den Parameterwerten (im Vergleich zu verketteten SQL-Zeichenfolgen), verbessern die Lesbarkeit und helfen Codewartern, Eingaben und Ausgaben der Abfrage schnell zu verstehen.
Kann in Java getMetadata () und getParameterMetadata () aufrufen, um die Felder der Ergebnismenge bzw. der Parameter zu berücksichtigen
Akzeptiert in Java intelligent Java Objekte als Parametertypen über setObject, setBoolean, setByte, setDate, setDouble, setDouble, setFloat, setInt, setLong, setShort, setTime, setTimestamp - es wird in das JDBC-Typformat konvertiert das ist für DB nachvollziehbar (nicht nur für das Format toString ()).
Akzeptiert in Java SQL ARRAYs als Parametertyp über die setArray-Methode
Akzeptiert in Java CLOBs, BLOBs, OutputStreams und Readers als Parameter "feeds" über die Methoden setClob/setNClob, setBlob, setBinaryStream, setCharacterStream/setAsciiStream/setNCharacterStream
Ermöglicht in Java das Festlegen von DB-spezifischen Werten für SQL DATALINK, SQL ROWID, SQL XML und NULL über die Methoden setURL, setRowId, setSQLXML und setNull
Erbt in Java alle Methoden von Statement. Es erbt die addBatch-Methode und ermöglicht außerdem das Hinzufügen einer Reihe von Parameterwerten, die mit der addBatch-Methode mit der Reihe der gestapelten SQL-Befehle übereinstimmen.
In Java ermöglicht eine spezielle Art von PreparedStatement (die Unterklasse CallableStatement) die Ausführung gespeicherter Prozeduren - Unterstützung für hohe Leistung, Kapselung, prozedurale Programmierung und SQL, DB-Administration/Wartung/Optimierung der Logik und Verwendung proprietärer DB-Logik und -Funktionen
PreparedStatement
ist eine sehr gute Abwehr (aber nicht narrensicher) gegen SQL-Injection-Angriffe . Das Binden von Parameterwerten ist ein guter Schutz vor "kleinen Bobby-Tabellen" einem unerwünschten Besuch.
Einige der Vorteile von PreparedStatement gegenüber Statement sind:
Weitere Informationen zum Thema SQL Injection finden Sie unter http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example
nicht viel hinzuzufügen,
1 - Wenn Sie eine Abfrage in einer Schleife ausführen möchten (mehr als ein Mal), kann die vorbereitete Anweisung aufgrund der von Ihnen erwähnten Optimierung schneller sein.
Abfrage mit 2 Parametern ist eine gute Möglichkeit, SQL Injection zu vermeiden, das nur in PreparedStatement verfügbar ist.
Anweisung ist statisch und vorbereitete Anweisung ist dynamisch.
Anweisung ist für DDL und vorbereitete Anweisung für DML geeignet.
Die Anweisung ist langsamer, während die vorbereitete Anweisung schneller ist.
CLOBs können in einer Anweisung nicht ausgeführt werden.
Und: (OraclePreparedStatement) ps
sQL-Injektion wird von vorbereiteten Anweisungen ignoriert, sodass die Sicherheit in vorbereiteten Anweisungen erhöht wird
Zitat von mattjames
Die Verwendung einer Anweisung in JDBC sollte zu 100% für die Verwendung für DDL (ALTER, CREATE, GRANT usw.) lokalisiert sein, da dies die einzigen Anweisungstypen sind, die BIND VARIABLES nicht akzeptieren können. PreparedStatements oder CallableStatements sollten für JEDEN ANDEREN Anweisungstyp (DML, Abfragen) verwendet werden. Dies sind die Anweisungstypen, die Bindungsvariablen akzeptieren.
Dies ist eine Tatsache, eine Regel, ein Gesetz - verwenden Sie vorbereitete Aussagen ÜBERALL. Verwenden Sie STATEMENTS fast nirgendwo.
Die Anweisung wird zur Ausführung statischer SQL-Anweisungen verwendet und akzeptiert keine Eingabeparameter.
PreparedStatement wird zur mehrmaligen dynamischen Ausführung von SQL-Anweisungen verwendet. Es werden Eingabeparameter akzeptiert.
Ein weiteres Merkmal einer vorbereiteten oder parametrisierten Abfrage: Referenz aus diesem Artikel.
Diese Anweisung ist eines der Merkmale des Datenbanksystems, in dem dieselbe SQL-Anweisung wiederholt mit hoher Effizienz ausgeführt wird. Die vorbereiteten Anweisungen sind eine Art der Vorlage und werden von der Anwendung mit verschiedenen Parametern verwendet.
Die Anweisungsvorlage wird vorbereitet und an das Datenbanksystem gesendet, und das Datenbanksystem analysiert, kompiliert und optimiert diese Vorlage und speichert sie, ohne sie auszuführen.
Einige der Parameter, bei denen die Klausel bei der späteren Anwendung der Vorlagenerstellung nicht übergeben wird, senden diese Parameter an das Datenbanksystem. Das Datenbanksystem verwendet die Vorlage von SQL Statement und wird gemäß Anforderung ausgeführt.
Vorbereitete Anweisungen sind sehr nützlich für SQL Injection, da die Anwendung Parameter mit verschiedenen Techniken und Protokollen vorbereiten kann.
Wenn die Anzahl der Daten zunimmt und sich die Indizes zu diesem Zeitpunkt häufig ändern, können vorbereitete Anweisungen fehlschlagen, da in dieser Situation ein neuer Abfrageplan erforderlich ist.
Die Schnittstelle Statement
führt statische SQL-Anweisungen ohne Parameter aus
PreparedStatement
interface (extended Statement) führt eine vorkompilierte SQL-Anweisung mit/ohne Parameter aus
Effizient für wiederholte Ausführungen
Es ist vorkompiliert, damit es schneller ist
Keine Verwechslung: einfach merken
Ich habe alle Antworten auf diese Frage befolgt, um einen funktionierenden Legacy-Code mit - Statement
(aber mit SQL-Injections) in eine Lösung mit PreparedStatement
mit einem viel langsameren Code zu ändern, da ich die Semantik nicht richtig verstehe Statement.addBatch(String sql)
& PreparedStatement.addBatch()
.
Ich liste mein Szenario hier auf, damit andere nicht den gleichen Fehler machen.
Mein Szenario war
Statement statement = connection.createStatement();
for (Object object : objectList) {
//Create a query which would be different for each object
// Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();
Im obigen Code hatte ich Tausende verschiedener Abfragen, die alle zur selben Anweisung hinzugefügt wurden, und dieser Code arbeitete schneller, da Anweisungen, die nicht zwischengespeichert wurden, gut waren und dieser Code in der App selten ausgeführt wurde.
Um SQL Injections zu reparieren, änderte ich diesen Code in:
List<PreparedStatement> pStatements = new ArrayList<>();
for (Object object : objectList) {
//Create a query which would be different for each object
PreparedStatement pStatement =connection.prepareStatement(query);
// This query can't be added to batch because its a different query so I used list.
//Set parameter to pStatement using object
pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately
for (PreparedStatement ps : pStatements) {
ps.executeUpdate();
}
Wie Sie sehen, habe ich angefangen, Tausende von PreparedStatement
Objekten zu erstellen und konnte dann schließlich die Stapelverarbeitung nicht mehr verwenden, da mein Szenario voraussetzte, dass - Tausende von UPDATE- oder INSERT-Abfragen und alle vorhanden sind Diese Abfragen sind zufällig unterschiedlich.
Das Korrigieren der SQL-Injection war obligatorisch, ohne dass dies zu Leistungseinbußen führen würde, und ich glaube nicht, dass dies in diesem Szenario mit PreparedStatement
möglich ist.
Wenn Sie die eingebaute Stapelverarbeitung verwenden, müssen Sie sich auch darum kümmern, nur eine Anweisung zu schließen. Bei diesem Listenansatz müssen Sie jedoch die Anweisung schließen, bevor Sie sie wiederverwenden. Wiederverwenden einer PreparedStatement