Statement ve PreparedStatement arasındaki fark


222

Hazırlanan İfadeler, İfadenin biraz daha güçlü bir versiyonudur ve her zaman bir İfade kadar hızlı ve kullanımı kolay olmalıdır.
Hazırlanan İfadeler parametrelerle ifade edilebilir

İlişkisel veritabanlarının çoğu JDBC / SQL sorgusunu dört adımda işler:

  1. Gelen SQL sorgusunu ayrıştırma
  2. SQL sorgusunu derleyin
  3. Veri toplama yolunu planlama / optimize etme
  4. Optimize edilmiş sorguyu yürütün / veri alın ve döndürün

Bir Bildirim her zaman veritabanına gönderilen her SQL sorgusu için yukarıdaki dört adım boyunca ilerler. Hazırlanan Bir İfade, yukarıdaki yürütme sürecinde (1) - (3) adımlarını önceden yürütür. Bu nedenle, bir Hazırlanmış İfade oluştururken, bazı ön optimizasyon hemen gerçekleştirilir. Etki, yürütme zamanında veritabanı altyapısı üzerindeki yükü azaltmaktır.

Şimdi sorum şu: "Hazırlanan İfadeyi kullanmanın başka bir avantajı var mı?"


12
bana göre en verimli olanı, sorgunuzun dinamik olarak parametrelenebilmesidir
Hussain Akhtar Wahid 'Ghouri'

Yanıtlar:


198

Bir Avantajları PreparedStatement:

  • SQL deyiminin önceden derlenmesi ve DB tarafında önbelleğe alınması, genel olarak daha hızlı yürütmeye ve aynı SQL deyimini toplu olarak yeniden kullanma yeteneğine yol açar .

  • Tırnaklardan ve diğer özel karakterlerden yerleşik olarak kaçarak SQL enjeksiyon saldırılarının otomatik olarak önlenmesi . Bunun PreparedStatement setXxx(), değerleri ayarlamak için yöntemlerden herhangi birini kullanmanızı gerektirdiğini unutmayın.

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

    ve böylece yapamaz satır içi dize birleştirerek SQL dizesinde değerleri.

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
    preparedStatement.executeUpdate();
    
  • Bir SQL dizede standart dışı Java nesneleri ayarı örn kolaylaştırır Date, Time, Timestamp, BigDecimal, InputStream( Blob) ve Reader( Clob). Bu türlerin çoğunda, toString()basitçe yaptığınız gibi "sadece" yapamazsınız Statement. Hatta PreparedStatement#setObject(), aşağıdaki yardımcı program yönteminde gösterildiği gibi, bir döngü içinde kullanmak için hepsini yeniden düzenleyebilirsiniz :

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

    Hangi aşağıdaki gibi kullanılabilir:

    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
Tanımlayıcı ve açıklayıcı bir metin, referanslar ve örnekle birleştiğinde mükemmel bir cevap verir. +1
XenoRo

1
@RD Bu doğru olabilir, çünkü hazırlanmış bir ifade veritabanına 2 gidiş-dönüş gerektirir: birincisi hazırlanır, ikincisi yürütür. Ancak, test ediyorum. Planın hala bir veritabanı sunucusunda önbelleğe alınacağını varsayıyorum Statement, ancak bir teste değer olabilir.
Brandon

2
Java ile kesin olarak söyleyemem, ancak genel olarak hazırlanmış bir ifade "tırnak ve diğer özel karakterlerin yerleşik kaçan" preform değil ; bunun yerine, SQL bir sorgu planına dönüştürüldükten sonra parametreleri DBMS'ye ayrı bilgi paketleri olarak göndererek yürütülebilir SQL ve verilerin ayrılmasını gerçekleştirir .
IMSoP

@BalusC - Detaylı açıklama için teşekkür ederim.
CodeBee ..

49
  1. Önceden derlenmiştir (bir kez), dinamik SQL'in tekrar tekrar yürütülmesi için daha hızlıdır (parametrelerin değiştiği yerlerde)

  2. Veritabanı deyiminin önbelleğe alınması DB yürütme performansını artırır

    Veritabanları, daha önce yürütülen ifadeler için yürütme planlarının önbelleklerini saklar. Bu, veritabanı motorunun daha önce yürütülen ifadelerin planlarını yeniden kullanmasına izin verir. PreparedStatement parametreler kullandığından, her yürütüldüğünde aynı SQL olarak görünür, veritabanı önceki erişim planını yeniden kullanabilir ve işlemeyi azaltır. İfadeler parametreleri SQL dizesine "satır içi" yapar ve bu nedenle DB ile aynı SQL olarak görünmez, böylece önbellek kullanımını önler.

  3. İkili iletişim protokolü daha az bant genişliği ve DB sunucusuna daha hızlı iletişim anlamına gelir

    Hazırlanan ifadeler normalde SQL olmayan bir ikili protokol aracılığıyla yürütülür. Bu, paketlerde daha az veri olduğu anlamına gelir, bu nedenle sunucu ile iletişim daha hızlı olur. Temel olarak ağ işlemleri, bellek içi CPU işlemlerinden daha yavaş bir büyüklük sırası olan disk işlemlerinden daha yavaş bir büyüklük sırasıdır. Bu nedenle, ağ üzerinden gönderilen veri miktarındaki herhangi bir azalmanın genel performans üzerinde iyi bir etkisi olacaktır.

  4. Sağlanan tüm parametre değerleri için metin kaçarak SQL enjeksiyonuna karşı koruma sağlarlar.

  5. Sorgu kodu ve parametre değerleri (birleştirilmiş SQL dizelerine kıyasla) arasında daha güçlü bir ayırma sağlar, okunabilirliği artırır ve kod sahiplerinin sorgunun girişlerini ve çıktılarını hızlı bir şekilde anlamalarına yardımcı olur.

  6. Java'da, sonuç kümesi alanlarını ve parametre alanlarını sırasıyla yansıtmak için getMetadata () ve getParameterMetadata () yöntemlerini çağırabilirsiniz.

  7. Java'da, java nesnelerini setObject, setBoolean, setByte, setDate, setDouble, setDouble, setFloat, setInt, setLong, setShort, setTime, setTimestamp yoluyla akıllıca parametre türleri olarak kabul eder - DB ile anlaşılabilir JDBC tür biçimine dönüştürür (sadece toString değil) () biçim).

  8. Java'da, SQL ARRAY'lerini setArray yöntemiyle parametre türü olarak kabul eder

  9. Java'da, sırasıyla setClob / setNClob, setBlob, setBinaryStream, setCharacterStream / setAsciiStream / setNCharacterStream yöntemleri aracılığıyla CLOB'leri, BLOB'ları, OutputStreams ve Okuyucular parametresini "feeds" parametresi olarak kabul eder

  10. Java'da, setURL, setRowId, setSQLXML ve setNull yöntemleri aracılığıyla SQL'e özgü değerlerin SQL DATALINK, SQL ROWID, SQL XML ve NULL için ayarlanmasına izin verir

  11. Java'da, Deyim'den tüm yöntemleri devralır. AddBatch yöntemini devralır ve ek olarak, eklenen SQL komutları kümesini addBatch yöntemiyle eşleştirmek için bir dizi parametre değeri eklenmesine izin verir.

  12. Java'da, özel bir PreparedStatement türü (CallableStatement alt sınıfı) saklanır yordamların yürütülmesine izin verir - yüksek performans, kapsülleme, yordamsal programlama ve SQL, DB yönetimi / bakımı / ayarlaması ve özel DB mantık ve özelliklerinin kullanımını destekler


Her ikisi de sadece arayüzeyken tüm bu harikalar nasıl mümkün olabilir?!?!
Rafael

1
'Harikalar', arayüzlerin (satıcıya özgü) uygulamalarını döndüren standart fabrika yöntemleri ile mümkün olur: Connection.createStatement ve Connection.prepareStatement. Bu tasarım sizi arabirimlere karşı çalışmaya zorlar, böylece belirli uygulama sınıflarını bilmenize ve bu uygulama sınıflarıyla gereksiz sıkı bağlantılardan kaçınmanıza gerek kalmaz. Tüm bunlar Java jdbc belgeleri ve Java belgelerindeki örneklerle açıklanmıştır. :)
Glen Best

"Başparmak kuralı" parçanızın bir anlamı yok 🤔
bhathiya-perera

38

PreparedStatementSQL enjeksiyon saldırılarını önlemede çok iyi bir savunmadır (ancak kusursuz değildir) . Parametre değerlerini bağlamak , istenmeyen bir ziyaret yaparak "küçük Bobby Tabloları" na karşı korunmanın iyi bir yoludur .


6
Hazırlanan bir ifade ile SQL enjeksiyonu nasıl yapılır?
Michael Borgwardt

2
Michael, Hazırlanan ifadelere argüman olarak iletilen değişkenler JDBC sürücüsü tarafından otomatik olarak kaçacaktır.
CodeBee ..

3
Bir SQL enjeksiyon saldırısının hazırlanmış bir ifadeye karşı nasıl çalışacağına bir örnek verebilir misiniz? Veritabanı kodunda bir hata olduğunu mu düşünüyorsunuz?
Peter Recore

2
Evet, ama "oldukça aptal" ın çok ötesinde. Aptallığı üflemek akıl. Onsluk bir bilgiye sahip hiç kimse bunu yapmazdı.
duffymo

2
Ayrıca, çok sayıda veritabanı sunucuları sütun adlarını (düşünmek paremetreleme desteklemez ORDER BY) belirli yerlerde (düşünmek ve / veya sayısal sabitler LIMIT, OFFSETbu Hazırlanan Tablolar ve ölçülebilirliği yerde kullanılsa bile, SQL enjeksiyonu tarafından saldırıya böylece ve diğer sayfalama çözümleri) mümkün.
dnet

31

PreparedStatement'ın Beyan Üzerinden Bazı Faydaları:

  1. PreparedStatement, SQL karakter saldırılarını önlememize yardımcı olur, çünkü özel karakterlerden otomatik olarak kaçar.
  2. PreparedStatement, parametre girişleriyle dinamik sorgular yürütmemizi sağlar.
  3. PreparedStatement, sorgu için giriş parametrelerini ayarlamak üzere farklı ayarlayıcı yöntemleri sunar.
  4. PreparedStatement, Deyimden daha hızlıdır. PreparedStatement'ı yeniden kullandığımızda veya birden çok sorguyu yürütmek için toplu işleme yöntemlerini kullandığımızda daha görünür hale gelir.
  5. PreparedStatement, set yönlendirici yöntemlerle nesne Odaklı kod yazmamıza yardımcı olurken, Deyim ile sorgu oluşturmak için Dize Birleştirme kullanmamız gerekir. Ayarlanacak birden çok parametre varsa, String birleştirmesini kullanarak Query yazmak çok çirkin ve hataya açık görünür.

SQL enjeksiyon sorunu hakkında daha fazla bilgiyi http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example adresinde bulabilirsiniz.


Makaleni okudum, gerçekten güzel bir makale. Benim sorum şimdi kimse neden Deyim kullanmaktır ?! statik bir sorgu için bile ?!
pedram bashiri

Her zaman PreparedStatement kullanıyorum, Statement'in daha fazla fayda sağlayabileceği belirli bir senaryo bilmiyorum.
Pankaj

13

ekleyecek çok şey yok,

1 - bir sorguyu döngüde (1 kereden fazla) yürütmek istiyorsanız, belirttiğiniz optimizasyon nedeniyle hazırlanmış ifade daha hızlı olabilir.

2 - parametreli sorgu SQL Enjeksiyonu önlemek için iyi bir yoldur. Parametrelendirilmiş sorgular yalnızca PreparedStatement'ta kullanılabilir.


10

İfade statiktir ve hazırlanan ifade dinamiktir.

İfade DDL için uygundur ve DML için hazırlanmış beyan.

Hazırlanan ifade daha hızlı olurken, ifade daha yavaştır.

daha fazla fark (arşivlendi)


7

Bir İfadede CLOB yapılamaz.

Ve: (OraclePreparedStatement) ps


7

Alıntı sahibi: mattjames

JDBC'de bir Deyimin kullanımı DDL (ALTER, CREATE, GRANT vb.) İçin kullanılmak üzere% 100 yerelleştirilmelidir, çünkü bunlar BIND DEĞİŞKENLERİNİ kabul edemeyen tek ifade tipidir. PreparedStatements veya CallableStatements, HER DİĞER deyim türü (DML, Sorgular) için kullanılmalıdır. Bunlar, bind değişkenlerini kabul eden ifade tipleridir.

Bu bir gerçektir, bir kural, kanun kullanımı her yerde. STATEMENTS kullanın neredeyse hiçbir yerde.


5

sql enjeksiyonu hazırlanan ifadeyle göz ardı edildiğinden, hazırlanan ifadede güvenlik artar


4
  • Okuması daha kolay
  • Sorgu dizesini kolayca sabit hale getirebilirsiniz

4

Deyim statik SQL deyimlerini yürütmek için kullanılır ve girdi parametrelerini kabul edemez.

PreparedStatement, SQL ifadelerini birçok kez dinamik olarak yürütmek için kullanılacaktır. Giriş parametrelerini kabul edecektir.


4

Hazırlanmış veya Parametrelendirilmiş Sorgu'nun bir başka özelliği: Bu makaleden alınan referans.

Bu ifade, aynı SQL ifadesinin yüksek verimlilikle tekrar tekrar yürütüldüğü veritabanı sisteminin özelliklerinden biridir. Hazırlanan ifadeler Şablonun bir türüdür ve farklı parametrelerle uygulama tarafından kullanılır.

İfade şablonu hazırlanır ve veritabanı sistemine gönderilir ve veritabanı sistemi bu şablon üzerinde ayrıştırma, derleme ve optimizasyon gerçekleştirir ve yürütmeden saklar.

Şablon oluşturma sonraki uygulama sırasında cümlenin geçmediği gibi bazı parametreler, bu parametreleri veritabanı sistemine gönderir ve SQL Deyimi veritabanı sistemi kullanım şablonunu kullanır ve isteğe göre yürütür.

Hazırlanan ifadeler, uygulama farklı teknikler ve protokoller kullanarak parametre hazırlayabildiğinden SQL Injection'a karşı çok faydalıdır.

Veri sayısı arttığında ve dizinler o sırada sık sık değiştiğinde, bu durumda yeni bir sorgu planı gerektirdiği için Hazırlanan Deyimler başarısız olabilir.


3

Statement arabirimi statik SQL deyimlerini parametre olmadan yürütür

PreparedStatement interface (Extement Expement), parametreli / parametresiz önceden derlenmiş bir SQL ifadesi yürütür

  1. Tekrarlanan yürütmeler için verimli

  2. Önceden derlenmiştir, böylece daha hızlıdır


2

Karışıklık yok: sadece hatırla

  1. Deyim DDL'ler gibi statik sorgular için kullanılır, yani create, drop, alter ve preparStatement dinamik sorgular yani DML sorgusu için kullanılır.
  2. Deyimde, sorgu preparStatement içinde önceden derlenmiş değil çünkü bu preparStatement zaman verimli olduğundan sorgu önceden derlenir.
  3. Deyim argüman almazken, preparStatement oluşturma sırasında argüman alır. Örneğin, tablo oluşturmak ve öğe eklemek istiyorsanız :: Deyimi kullanarak tablo oluştur (statik) ve preparStatement kullanarak öğe ekle (dinamik).

1
Deyim argüman almazken, hazırlık durumu oluşturma sırasında argüman alır.

1

- Bir çalışma eski kodu kullanarak değiştirmek için bu sorunun tüm cevapları takip Statementkullanarak bir çözüme (SQL Enjeksiyonlar ancak sahip) PreparedStatementçünkü etrafında semantik kötü anlayış çok daha yavaş koduyla Statement.addBatch(String sql)& PreparedStatement.addBatch().

Bu yüzden senaryomu burada listeliyorum, böylece diğerleri aynı hatayı yapmıyor.

Senaryom

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

Bu yüzden yukarıdaki kodda, hepsi aynı ifadeye eklenen binlerce farklı sorgu vardı ve önbelleğe alınmayan ifadeler iyi olduğundan ve bu kod uygulamada nadiren yürütüldüğünden bu kod daha hızlı çalıştı.

Şimdi SQL Enjeksiyonlarını düzeltmek için bu kodu

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

Gördüğünüz gibi, binlerce PreparedStatementnesne oluşturmaya başladım ve sonunda senaryoyu kullanamadım çünkü senaryom bunu istedi - binlerce GÜNCELLEME veya INSERT sorgusu var ve bu sorguların hepsi farklı oluyor.

SQL enjeksiyonunu sabitlemek hiçbir performans düşüşü pahasına zorunluydu ve PreparedStatementbu senaryoda bunun mümkün olduğunu düşünmüyorum .

Ayrıca, dahili toplu iş özelliğini kullandığınızda, yalnızca bir Deyimi kapatma konusunda endişelenmeniz gerekir, ancak bu Liste yaklaşımıyla, yeniden kullanmadan önce ifadeyi kapatmanız gerekir, bir PreparedStatement'ı Yeniden Kullanmanız gerekir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.