Perbedaan antara Statement dan PreparedStatement


222

Pernyataan Disiapkan adalah versi Pernyataan yang sedikit lebih kuat, dan harus selalu setidaknya secepat dan mudah ditangani sebagai Pernyataan.
Pernyataan Disiapkan dapat parametrized

Sebagian besar basis data relasional menangani permintaan JDBC / SQL dalam empat langkah:

  1. Parsing kueri SQL yang masuk
  2. Kompilasi permintaan SQL
  3. Merencanakan / mengoptimalkan jalur akuisisi data
  4. Jalankan permintaan / perolehan dan pengembalian data yang dioptimalkan

Pernyataan akan selalu diproses melalui empat langkah di atas untuk setiap permintaan SQL yang dikirim ke database. Pernyataan Disiapkan pra-eksekusi langkah (1) - (3) dalam proses eksekusi di atas. Jadi, ketika membuat Pernyataan Disiapkan, beberapa pra-optimasi dilakukan segera. Efeknya adalah untuk mengurangi beban pada mesin database pada waktu eksekusi.

Sekarang pertanyaan saya adalah - "Apakah ada keuntungan lain dari menggunakan Pernyataan Disiapkan?"


12
yang paling efisien menurut saya adalah bahwa kueri Anda dapat diparameterisasi secara dinamis
Hussain Akhtar Wahid 'Ghouri'

Jawaban:


198

Keuntungan a PreparedStatement :

  • Precompilation dan caching sisi-DB dari pernyataan SQL mengarah pada keseluruhan eksekusi yang lebih cepat dan kemampuan untuk menggunakan kembali pernyataan SQL yang sama dalam batch .

  • Pencegahan serangan injeksi SQL secara otomatis dengan menghilangkan tanda kutip dan karakter khusus lainnya. Perhatikan bahwa ini mengharuskan Anda menggunakan salah satu dariPreparedStatement setXxx() metode untuk menetapkan nilai

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

    dan dengan demikian tidak menyejajarkan nilai-nilai dalam string SQL dengan string-concatenating.

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
    preparedStatement.executeUpdate();
  • Meringankan pengaturan benda Jawa non-standar dalam string SQL, misalnya Date, Time, Timestamp, BigDecimal, InputStream( Blob) dan Reader( Clob). Pada sebagian besar tipe tersebut, Anda tidak dapat "hanya" melakukan toString()seperti yang Anda lakukan secara sederhana Statement. Anda bahkan bisa memperbaiki semuanya untuk menggunakan PreparedStatement#setObject()di dalam loop seperti yang ditunjukkan dalam metode utilitas di bawah ini:

    public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
        for (int i = 0; i < values.length; i++) {
            preparedStatement.setObject(i + 1, values[i]);
        }
    }

    Yang bisa digunakan seperti di bawah ini:

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

4
Teks deskriptif dan menjelaskan, ditambah dengan referensi dan contoh, membuat jawaban yang sangat baik. +1
XenoRo

1
@RD Ini mungkin benar karena pernyataan yang dipersiapkan membutuhkan 2 perjalanan pulang pergi ke database: yang pertama untuk dipersiapkan, yang kedua untuk dieksekusi. Namun, saya akan mengujinya. Saya kira rencana itu masih akan di-cache di server database untuk Statement, tetapi mungkin layak untuk diuji.
Brandon

2
Saya tidak bisa mengatakan dengan pasti dengan Java, tetapi secara umum pernyataan yang disiapkan tidak membentuk "built-in pelepasan tanda kutip dan karakter khusus lainnya"; alih-alih, ia melakukan pemisahan SQL yang dapat dieksekusi dan data , mengirimkan parameter ke DBMS sebagai paket informasi yang terpisah setelah SQL telah dikonversi menjadi rencana kueri.
IMSoP

@BalusC - Terima kasih atas penjelasan terperinci.
CodeBee ..

49
  1. Mereka sudah dikompilasi sebelumnya (sekali), jadi lebih cepat untuk eksekusi SQL dinamis berulang (di mana parameter berubah)

  2. Caching pernyataan basis data meningkatkan kinerja eksekusi DB

    Pangkalan data menyimpan cache rencana eksekusi untuk pernyataan yang dieksekusi sebelumnya. Ini memungkinkan mesin basis data untuk menggunakan kembali rencana untuk pernyataan yang telah dieksekusi sebelumnya. Karena PreparedStatement menggunakan parameter, setiap kali dieksekusi muncul sebagai SQL yang sama, database dapat menggunakan kembali rencana akses sebelumnya, mengurangi pemrosesan. Pernyataan "inline" parameter ke string SQL dan tidak muncul sebagai SQL yang sama dengan DB, mencegah penggunaan cache.

  3. Protokol komunikasi biner berarti lebih sedikit bandwidth dan lebih cepat melakukan panggilan ke server DB

    Pernyataan yang disiapkan biasanya dieksekusi melalui protokol biner non-SQL. Ini berarti bahwa ada lebih sedikit data dalam paket, sehingga komunikasi ke server lebih cepat. Sebagai aturan praktis operasi jaringan adalah urutan besarnya lebih lambat dari operasi disk yang urutan besarnya lebih lambat dari operasi CPU dalam memori. Oleh karena itu, setiap pengurangan jumlah data yang dikirim melalui jaringan akan memiliki efek yang baik pada kinerja keseluruhan.

  4. Mereka melindungi terhadap injeksi SQL, dengan menghindari teks untuk semua nilai parameter yang disediakan.

  5. Mereka memberikan pemisahan yang lebih kuat antara kode kueri dan nilai parameter (dibandingkan dengan string SQL gabungan), meningkatkan keterbacaan dan membantu pengelola kode dengan cepat memahami input dan output dari kueri.

  6. Di java, bisa memanggil getMetadata () dan getParameterMetadata () untuk merefleksikan bidang hasil set dan bidang parameter, masing-masing

  7. Di java, secara cerdas menerima objek java sebagai tipe parameter melalui setObject, setBoolean, setByte, setDate, setDouble, setDloat, setFloat, setInt, setLong, setShort, setTimestamp - ini dikonversi ke dalam format tipe JDBC yang dapat dipahami oleh DB (tidak hanya toString () format).

  8. Di java, menerima SQL ARRAYs, sebagai tipe parameter melalui metode setArray

  9. Di java, masing-masing menerima CLOBs, BLOBs, OutputStreams, dan Pembaca sebagai parameter "feed" melalui setClob / setNClob, setBlob, setBinaryStream, setCharacterStream / setAsciiStream / metode setNCharacterStream, secara berurutan

  10. Di java, memungkinkan nilai-nilai spesifik DB untuk diatur untuk SQL DATALINK, SQL ROWID, SQL XML, dan NULL melalui setURL, setRowId, setSQLXML, dan metode setNull

  11. Di java, mewarisi semua metode dari Pernyataan. Itu mewarisi metode addBatch, dan juga memungkinkan satu set nilai parameter yang akan ditambahkan agar sesuai dengan set perintah SQL batch melalui metode addBatch.

  12. Di java, jenis khusus PreparedStatement (subclass CallableStatement) memungkinkan prosedur tersimpan untuk dieksekusi - mendukung kinerja tinggi, enkapsulasi, pemrograman prosedural dan SQL, administrasi DB / pemeliharaan / tweaker logika, dan penggunaan logika & fitur DB eksklusif


Bagaimana semua keajaiban itu mungkin terjadi ketika keduanya hanya antarmuka?!?!
Rafael

1
'Keajaiban' dimungkinkan melalui metode pabrik standar yang mengembalikan implementasi (khusus vendor) antarmuka: Connection.createStatement dan Connection.prepareStatement. Desain ini memaksa Anda untuk bekerja melawan antarmuka sehingga Anda tidak perlu tahu kelas implementasi spesifik dan untuk menghindari penggabungan erat yang tidak perlu dengan kelas implementasi tersebut. Semua dijelaskan dengan contoh dalam Java jdbc docs & Java docs. :)
Glen Best

Bagian "sebagai aturan praktis" Anda tidak masuk akal bukan sebaliknya
bhathiya-perera

38

PreparedStatementadalah pertahanan yang sangat baik (tapi tidak mudah) dalam mencegah serangan injeksi SQL . Mengikat nilai parameter adalah cara yang baik untuk menjaga terhadap "Bobby Tables kecil" melakukan kunjungan yang tidak diinginkan.


6
Bagaimana orang melakukan injeksi SQL melalui pernyataan yang disiapkan?
Michael Borgwardt

2
Michael, Variabel yang diteruskan sebagai argumen untuk pernyataan yang disiapkan secara otomatis akan lolos oleh driver JDBC.
CodeBee ..

3
Bisakah Anda memberikan contoh bagaimana serangan injeksi SQL akan bekerja terhadap pernyataan yang disiapkan? Apakah Anda mengasumsikan bug dalam kode basis data?
Peter Recore

2
Ya, tapi itu jauh melampaui "cukup bodoh". Itu kebodohan yang bertiup. Tak seorang pun dengan satu ons pengetahuan akan melakukan itu.
duffymo

2
Juga, banyak vendor database tidak mendukung nama kolom parameterisasi (think ORDER BY) dan / atau konstanta numerik di tempat-tempat tertentu (think LIMIT, OFFSETdan solusi pagination lainnya), sehingga ini dapat diserang oleh injeksi SQL, bahkan ketika Pernyataan dan parameterisasi Disiapkan digunakan di mana pun bisa jadi.
dnet

31

Beberapa manfaat dari PreparedStatement atas Pernyataan adalah:

  1. PreparedStatement membantu kita mencegah serangan injeksi SQL karena secara otomatis lolos dari karakter khusus.
  2. PreparedStatement memungkinkan kita untuk mengeksekusi query dinamis dengan input parameter.
  3. PreparedStatement menyediakan berbagai jenis metode penyetel untuk mengatur parameter input untuk kueri.
  4. PreparedStatement lebih cepat dari Pernyataan. Itu menjadi lebih terlihat ketika kita menggunakan kembali PreparedStatement atau menggunakan metode pemrosesan batch untuk mengeksekusi beberapa kueri.
  5. PreparedStatement membantu kita dalam menulis kode berorientasi objek dengan metode setter sedangkan dengan Pernyataan kita harus menggunakan String Concatenation untuk membuat kueri. Jika ada beberapa parameter untuk ditetapkan, penulisan Query menggunakan String concatenation terlihat sangat jelek dan rawan kesalahan.

Baca lebih lanjut tentang masalah injeksi SQL di http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example


Saya membaca artikel Anda, sangat bagus. Pertanyaan saya sekarang adalah mengapa ada orang yang menggunakan Pernyataan ?! bahkan untuk permintaan statis ?!
pedram bashiri

Saya selalu menggunakan PreparedStatement, saya tidak tahu skenario khusus di mana Pernyataan mungkin lebih bermanfaat.
Pankaj

13

tidak banyak yang ditambahkan,

1 - jika Anda ingin menjalankan kueri dalam satu lingkaran (lebih dari 1 kali), pernyataan yang disiapkan dapat lebih cepat, karena optimasi yang Anda sebutkan.

2 - kueri parameterisasi adalah cara yang baik untuk menghindari SQL Injection. Permintaan parameter hanya tersedia di PreparedStatement.


10

Pernyataan statis dan statment yang disiapkan dinamis.

Pernyataan cocok untuk DDL dan menyiapkan statment untuk DML.

Pernyataan lebih lambat sementara pernyataan yang disiapkan lebih cepat.

lebih banyak perbedaan (diarsipkan)


7

Tidak dapat melakukan CLOB dalam Pernyataan.

Dan: (OraclePreparedStatement) ps


7

Dikutip oleh mattjames

Penggunaan Pernyataan dalam JDBC harus 100% dilokalisasi untuk digunakan untuk DDL (ALTER, CREATE, GRANT, dll.) Karena ini adalah satu-satunya jenis pernyataan yang tidak dapat menerima BIND VARIABEL. PreparedStatements atau CallableStatements harus digunakan untuk SETIAP jenis pernyataan lainnya (DML, Kueri). Karena ini adalah tipe pernyataan yang menerima variabel bind.

Ini adalah fakta, aturan, hukum - gunakan pernyataan yang disiapkan DI MANA SAJA. Gunakan hampir tidak ada tempat.


5

sql injeksi diabaikan oleh pernyataan yang disiapkan sehingga keamanan ditingkatkan dalam pernyataan yang disiapkan


4
  • Lebih mudah dibaca
  • Anda dapat dengan mudah membuat string kueri menjadi konstan

4

Pernyataan akan digunakan untuk mengeksekusi pernyataan SQL statis dan tidak dapat menerima parameter input.

PreparedStatement akan digunakan untuk mengeksekusi pernyataan SQL berkali-kali secara dinamis. Ini akan menerima parameter input.


4

Karakteristik lain dari Permintaan Disiapkan atau Parameter: Referensi diambil dari artikel ini.

Pernyataan ini adalah salah satu fitur dari sistem database di mana pernyataan SQL yang sama dijalankan berulang kali dengan efisiensi tinggi. Pernyataan yang disiapkan adalah satu jenis Templat dan digunakan oleh aplikasi dengan parameter yang berbeda.

Templat pernyataan disiapkan dan dikirim ke sistem basis data dan sistem basis data melakukan parsing, kompilasi, dan optimisasi pada templat ini dan menyimpannya tanpa menjalankannya.

Beberapa parameter seperti, di mana klausa tidak dilewati selama pembuatan template aplikasi nanti, mengirim parameter ini ke sistem database dan sistem database menggunakan template dari SQL Statement dan dijalankan sesuai permintaan.

Pernyataan yang disiapkan sangat berguna terhadap SQL Injection karena aplikasi dapat menyiapkan parameter menggunakan berbagai teknik dan protokol.

Ketika jumlah data meningkat dan indeks sering berubah pada waktu itu, Laporan yang Disiapkan mungkin gagal karena dalam situasi ini memerlukan rencana kueri baru.


3

Statement antarmuka mengeksekusi pernyataan SQL statis tanpa parameter

PreparedStatement interface (extending Statement) mengeksekusi pernyataan SQL yang dikompilasi dengan / tanpa parameter

  1. Efisien untuk eksekusi berulang

  2. Ini dikompilasi sehingga lebih cepat


2

Jangan bingung: ingat saja

  1. Pernyataan digunakan untuk kueri statis seperti DDLs yaitu create, drop, alter, dan prepStatement digunakan untuk kueri dinamis yaitu kueri DML.
  2. Dalam Pernyataan, kueri tidak dikompilasi saat dalam persiapanStatement kueri dikompilasi, karena persiapanStatement ini efisien waktu.
  3. prepStatement mengambil argumen pada saat pembuatan sedangkan Pernyataan tidak mengambil argumen. Sebagai contoh jika Anda ingin membuat tabel dan menyisipkan elemen lalu :: Buat tabel (statis) dengan menggunakan pernyataan dan masukkan elemen (dinamis) dengan menggunakan prepStatement.

1
prepStatement mengambil argumen pada saat penciptaan sementara Pernyataan tidak mengambil argumen.?

1

Saya mengikuti semua jawaban dari pertanyaan ini untuk mengubah kode lama bekerja menggunakan - Statement(tetapi memiliki SQL Suntikan) untuk solusi menggunakan PreparedStatementdengan kode yang jauh lebih lambat karena pemahaman semantik sekitar Statement.addBatch(String sql)& kurang PreparedStatement.addBatch().

Jadi saya daftar skenario saya di sini sehingga orang lain tidak membuat kesalahan yang sama.

Skenario saya adalah

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

Jadi dalam kode di atas, saya memiliki ribuan pertanyaan yang berbeda, semua ditambahkan ke pernyataan yang sama dan kode ini bekerja lebih cepat karena pernyataan yang tidak di-cache bagus & kode ini jarang dieksekusi di app.

Sekarang untuk memperbaiki Suntikan SQL, saya mengubah kode ini menjadi,

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();
}

Jadi Anda lihat, saya mulai membuat ribuan PreparedStatementobjek & akhirnya tidak dapat memanfaatkan batching karena skenario saya menuntut - ada ribuan permintaan UPDATE atau INSERT & semua pertanyaan ini kebetulan berbeda.

Memperbaiki injeksi SQL adalah wajib tanpa biaya penurunan kinerja dan saya tidak berpikir itu mungkin terjadi PreparedStatementdalam skenario ini.

Juga, ketika Anda menggunakan fasilitas batching inbuilt, Anda harus khawatir tentang hanya menutup satu Pernyataan tetapi dengan pendekatan Daftar ini, Anda perlu menutup pernyataan sebelum digunakan kembali, Menggunakan Kembali PreparedStatement

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.