تنفيذ العبارات
تنفيذ العبارات
بمجرد الحصول على Connection فعّال، تأتي الخطوة التالية وهي إرسال SQL إلى قاعدة البيانات. تمنحك JDBC الواجهة Statement لهذا الغرض، وتقدّم طريقتين رئيسيتين للتنفيذ: executeQuery للقراءة، وexecuteUpdate للكتابة. إتقان الفارق بينهما — ومعرفة متى تستخدم كلًّا منهما — هو أساس كل تفاعل مع قاعدة البيانات ستكتبه.
إنشاء Statement
يُنشأ كائن Statement من كائن الاتصال. وهو أيضًا مورد يجب إغلاقه بعد الاستخدام، لذا استخدم try-with-resources:
Statement بين أكثر من خيط تنفيذ. يجب على كل خيط إنشاء نسخته الخاصة من Connection الخاص به.
executeQuery — قراءة البيانات
تُستخدم executeQuery(String sql) لعبارات SQL التي تُعيد مجموعة نتائج — وتكون غالبًا SELECT. تُعيد كائنًا من نوع ResultSet تتنقّل عبره للوصول إلى الصفوف. تُطلق الطريقة SQLException إن كانت SQL مشوّهة أو رفضتها قاعدة البيانات، وتُطلقها أيضًا إن مرّرت عبارة DML (مثل UPDATE) بدلًا من استعلام قراءة.
نقاط مهمة حول executeQuery:
- تُعيد كائن
ResultSet— لن يكونnullأبدًا، لكنه قد يكون فارغًا (صفر صفوف). - يبدأ مؤشر
ResultSetقبل الصف الأول؛ استدعِrs.next()للتقدّم. - يُعدّ
ResultSetبدوره موردًا — اجعله داخل try-with-resources أو أغلقه صراحةً. - إغلاق
Statementيُغلق تلقائيًا الـResultSetالمرتبط به.
executeUpdate — كتابة البيانات
تُستخدم executeUpdate(String sql) لأي عبارة تُعدّل البيانات أو البنية: INSERT وUPDATE وDELETE وCREATE TABLE وDROP TABLE وما شابهها. تُعيد قيمة int تمثّل عدد الصفوف المتأثرة. لعبارات DDL مثل CREATE تكون القيمة المُعادة دائمًا 0.
0، فجملة WHERE لم تُطابق شيئًا. تجاهل هذه القيمة يُخفي الخطأ المنطقي في صمت.
الطريقة execute — البديل الشامل
تتوفّر طريقة ثالثة هي execute(String sql) تتعامل مع أي عبارة SQL بصرف النظر عمّا تُعيده. تُعيد boolean: القيمة true تعني أن النتيجة الأولى ResultSet، وfalse تعني أنها عدد صفوف مُحدَّثة. تستدعي بعدها stmt.getResultSet() أو stmt.getUpdateCount() وفقًا لذلك.
في الممارسة العملية تُستخدم execute حين تشغّل SQL ديناميكية أو مُدخَلة من المستخدم لا تعرف نوعها وقت الترجمة — كأداة إدارة قواعد بيانات تقبل استعلامات عشوائية. في كود التطبيقات العادية، افضّل executeQuery أو executeUpdate لأن النية تكون واضحة والمُجمّع يكشف أي استخدام خاطئ.
لماذا لا نخلط بينهما؟
استدعاء executeQuery بعبارة غير SELECT، أو executeUpdate باستعلام SELECT، يُسفر عن SQLException وقت التشغيل (يتفاوت السلوك التفصيلي من مشغّل لآخر، لكن الاعتماد عليه خطأ). كذلك يُعبّر التمييز عن النية لكل مطوّر يقرأ كودك: هذا الفرع يقرأ، ذاك يكتب. ويتناسب ذلك مع تجمّع الاتصالات وتوجيه القراءة إلى نسخة النسخ، حيث تذهب القراءات إلى النسخة وتذهب الكتابات إلى الخادم الأساسي.
PreparedStatement الذي يمنع حقن SQL ويجب استخدامه مع أي بيانات خارجية.
استرداد المفاتيح المُوَلَّدة تلقائيًا
عند تنفيذ INSERT في جدول يحتوي على مفتاح أساسي بزيادة تلقائية، كثيرًا ما تحتاج إلى المعرّف المُولَّد فورًا. مرّر العلم Statement.RETURN_GENERATED_KEYS إلى executeUpdate، ثم اقرأ المفتاح من ResultSet خاص:
الخلاصة
executeQuery— لعبارات SELECT؛ تُعيدResultSet.executeUpdate— لعبارات INSERT / UPDATE / DELETE / DDL؛ تُعيد عدد الصفوف.execute— البديل الشامل حين لا يُعرف نوع SQL وقت الترجمة.- أغلق
StatementوResultSetدائمًا باستخدام try-with-resources. - استخدم
RETURN_GENERATED_KEYSلاسترداد المعرّفات المُنشأة تلقائيًا بعد INSERT. - انتقل إلى
PreparedStatementفور دخول بيانات المستخدم في SQL — لا تدمجها أبدًا بتسلسل النصوص.