web-dev-qa-db-de.com

Unterschied zwischen Anweisung und PreparedStatement

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:

  1. Analysieren Sie die eingehende SQL-Abfrage
  2. Kompilieren Sie die SQL-Abfrage
  3. Planen/optimieren Sie den Datenerfassungspfad
  4. Führen Sie die optimierte Abfrage/Erfassung und Rückgabe von Daten aus

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?"

207
CodeBee..

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 PreparedStatementsetXxx() 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();
    
187
BalusC
  1. Sie sind (einmalig) vorkompiliert, daher schneller für die wiederholte Ausführung von dynamischem SQL (wo sich Parameter ändern).

  2. 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.

  3. 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.

  4. Sie schützen vor SQL-Injection, indem sie Text für alle bereitgestellten Parameterwerte maskieren.

  5. 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.

  6. Kann in Java getMetadata () und getParameterMetadata () aufrufen, um die Felder der Ergebnismenge bzw. der Parameter zu berücksichtigen

  7. 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 ()).

  8. Akzeptiert in Java SQL ARRAYs als Parametertyp über die setArray-Methode

  9. Akzeptiert in Java CLOBs, BLOBs, OutputStreams und Readers als Parameter "feeds" über die Methoden setClob/setNClob, setBlob, setBinaryStream, setCharacterStream/setAsciiStream/setNCharacterStream

  10. 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

  11. 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.

  12. 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

44
Glen Best

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.

36
duffymo

Einige der Vorteile von PreparedStatement gegenüber Statement sind:

  1. Mit PreparedStatement können wir SQL-Injection-Angriffe verhindern, da die Sonderzeichen automatisch ausgeblendet werden.
  2. Mit PreparedStatement können dynamische Abfragen mit Parametereingaben ausgeführt werden.
  3. PreparedStatement bietet verschiedene Arten von Setter-Methoden zum Festlegen der Eingabeparameter für die Abfrage.
  4. PreparedStatement ist schneller als Statement. Es wird sichtbarer, wenn wir das PreparedStatement wiederverwenden oder seine Stapelverarbeitungsmethoden zum Ausführen mehrerer Abfragen verwenden.
  5. PreparedStatement hilft uns beim Schreiben von objektorientiertem Code mit Setter-Methoden, wohingegen wir bei Statement die String-Verkettung verwenden müssen, um die Abfrage zu erstellen. Wenn mehrere Parameter festgelegt werden müssen, wirkt das Schreiben von Abfragen mit Zeichenfolgenverkettung sehr hässlich und fehleranfällig.

Weitere Informationen zum Thema SQL Injection finden Sie unter http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example

28
Pankaj

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.

13
mhshams

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.

mehr Unterschiede

10
sandeep vanama

CLOBs können in einer Anweisung nicht ausgeführt werden.

Und: (OraclePreparedStatement) ps

7
orbfish

sQL-Injektion wird von vorbereiteten Anweisungen ignoriert, sodass die Sicherheit in vorbereiteten Anweisungen erhöht wird

5
ashish geol

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.

4
Root
  • Es ist einfacher zu lesen
  • Sie können die Abfragezeichenfolge leicht zu einer Konstanten machen
4
nanda

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.

4
MARA MP

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.

4
Anvesh

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

  1. Effizient für wiederholte Ausführungen

  2. Es ist vorkompiliert, damit es schneller ist

3
Bernard

Keine Verwechslung: einfach merken

  1. Statement wird für statische Abfragen wie DDLs verwendet, d. H. Create, drop, alter und prepareStatement wird für dynamische Abfragen verwendet, d. H. DML-Abfragen.
  2. In Anweisung wird die Abfrage nicht vorkompiliert, während in prepareStatement die Abfrage vorkompiliert wird, da prepareStatement daher zeiteffizient ist.
  3. prepareStatement verwendet Argumente zum Zeitpunkt der Erstellung, während Statement keine Argumente verwendet. Wenn Sie zum Beispiel eine Tabelle erstellen und ein Element einfügen möchten, dann :: Erstellen Sie eine Tabelle (statisch) mit Statement und fügen Sie ein Element (dynamisch) mit prepareStatement ein.
1
Roopam

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

0
Sabir Khan