Perbedaan Antara Statement dan PreparedStatement

Java Top

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Ikhtisar

Dalam tutorial ini, kita akan mempelajari perbedaan antara antarmuka Pernyataan JDBC dan PreparedStatement . Kami tidak akan mencakup CallableStatement , antarmuka API JDBC yang digunakan untuk menjalankan prosedur yang tersimpan.

2. Antarmuka API JDBC

Baik Pernyataan dan PreparedStatement dapat digunakan untuk menjalankan kueri SQL. Antarmuka ini terlihat sangat mirip. Namun, mereka berbeda secara signifikan satu sama lain dalam fitur dan kinerja:

  • Pernyataan - Digunakan untuk menjalankan kueri SQL berbasis string
  • PreparedStatement - Digunakan untuk menjalankan kueri SQL berparameter

Untuk dapat menggunakan Statement dan PreparedStatement dalam contoh kami, kami akan mendeklarasikan konektor h2 JDBC sebagai dependensi dalam file pom.xml kami :

 com.h2database h2 1.4.200 

Mari tentukan entitas yang akan kita gunakan di seluruh artikel ini:

public class PersonEntity { private int id; private String name; // standard setters and getters }

3. Pernyataan

Pertama, antarmuka Pernyataan menerima string sebagai kueri SQL. Jadi, kode menjadi kurang terbaca saat kita menggabungkan string SQL:

public void insert(PersonEntity personEntity) { String query = "INSERT INTO persons(id, name) VALUES(" + personEntity.getId() + ", '" + personEntity.getName() + "')"; Statement statement = connection.createStatement(); statement.executeUpdate(query); }

Kedua, rentan terhadap injeksi SQL . Contoh berikut menggambarkan kelemahan ini.

Pada baris pertama, update akan menyetel kolom " name " pada semua baris menjadi " hacker ", karena apapun setelah "-" diinterpretasikan sebagai komentar dalam SQL dan kondisi pernyataan update akan diabaikan. Di baris kedua, penyisipan akan gagal karena kutipan di kolom " nama " belum di-escape:

dao.update(new PersonEntity(1, "hacker' --")); dao.insert(new PersonEntity(1, "O'Brien"))

Ketiga, JDBC meneruskan kueri dengan nilai inline ke database . Oleh karena itu, tidak ada pengoptimalan kueri, dan yang terpenting, mesin database harus memastikan semua pemeriksaan . Selain itu, kueri tidak akan muncul sama ke database dan akan mencegah penggunaan cache . Demikian pula, pembaruan batch perlu dijalankan secara terpisah:

public void insert(List personEntities) { for (PersonEntity personEntity: personEntities) { insert(personEntity); } }

Keempat, para Pernyataan antarmuka cocok untuk query DDL seperti MENCIPTAKAN, ALTER, dan DROP :

public void createTables() { String query = "create table if not exists PERSONS (ID INT, NAME VARCHAR(45))"; connection.createStatement().executeUpdate(query); }

Akhirnya, para Pernyataan antarmuka tidak dapat digunakan untuk menyimpan dan mengambil file dan array .

4. PreparedStatement

Pertama, PreparedStatement memperluas antarmuka Pernyataan . Ini memiliki metode untuk mengikat berbagai tipe objek , termasuk file dan array. Karenanya, kodenya menjadi mudah dipahami :

public void insert(PersonEntity personEntity) { String query = "INSERT INTO persons(id, name) VALUES( ?, ?)"; PreparedStatement preparedStatement = connection.prepareStatement(query); preparedStatement.setInt(1, personEntity.getId()); preparedStatement.setString(2, personEntity.getName()); preparedStatement.executeUpdate(); }

Kedua, melindungi dari injeksi SQL , dengan meng-escape teks untuk semua nilai parameter yang disediakan:

@Test void whenInsertAPersonWithQuoteInText_thenItNeverThrowsAnException() { assertDoesNotThrow(() -> dao.insert(new PersonEntity(1, "O'Brien"))); } @Test void whenAHackerUpdateAPerson_thenItUpdatesTheTargetedPerson() throws SQLException { dao.insert(Arrays.asList(new PersonEntity(1, "john"), new PersonEntity(2, "skeet"))); dao.update(new PersonEntity(1, "hacker' --")); List result = dao.getAll(); assertEquals(Arrays.asList( new PersonEntity(1, "hacker' --"), new PersonEntity(2, "skeet")), result); }

Ketiga, PreparedStatement menggunakan pra-kompilasi . Segera setelah database mendapatkan kueri, itu akan memeriksa cache sebelum melakukan pra-kompilasi kueri. Akibatnya, jika tidak di-cache, mesin database akan menyimpannya untuk penggunaan berikutnya.

Selain itu, fitur ini mempercepat komunikasi antara database dan JVM melalui protokol biner non-SQL. Artinya, ada lebih sedikit data dalam paket, sehingga komunikasi antar server berjalan lebih cepat.

Keempat, PreparedStatement menyediakan eksekusi batch selama koneksi database tunggal . Mari kita lihat ini beraksi:

public void insert(List personEntities) throws SQLException { String query = "INSERT INTO persons(id, name) VALUES( ?, ?)"; PreparedStatement preparedStatement = connection.prepareStatement(query); for (PersonEntity personEntity: personEntities) { preparedStatement.setInt(1, personEntity.getId()); preparedStatement.setString(2, personEntity.getName()); preparedStatement.addBatch(); } preparedStatement.executeBatch(); }

Selanjutnya, PreparedStatement menyediakan cara mudah untuk menyimpan dan mengambil file dengan menggunakan tipe data BLOB dan CLOB . Dengan nada yang sama, ini membantu untuk menyimpan daftar dengan mengubah java.sql.Array ke SQL Array.

Terakhir, PreparedStatement mengimplementasikan metode seperti getMetadata () yang berisi informasi tentang hasil yang dikembalikan.

5. Kesimpulan

Dalam tutorial ini, kami menyajikan perbedaan utama antara PreparedStatement dan Pernyataan . Kedua antarmuka menawarkan metode untuk mengeksekusi kueri SQL, tetapi lebih cocok menggunakan Pernyataan untuk kueri DDL dan PreparedStatement untuk kueri DML.

Seperti biasa, semua contoh kode tersedia di GitHub.

Jawa bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya