معالجة ResultSets
معالجة ResultSets
الـ ResultSet هو الكائن الذي يُعيده JDBC بعد تنفيذ أي استعلام SELECT. فكّر فيه كجدول قابل للتصفّح: يحتوي على مؤشر يبدأ قبل الصف الأول، وتُحرّكه صفًّا بصف (أو تقفز مباشرة إلى موضع محدد إن كان برنامج التشغيل يدعم ذلك). فهم كيفية التنقل بين الصفوف واستخراج القيم بأنواعها وفحص مخطّط النتائج في وقت التشغيل يجعلك أكثر كفاءة مع JDBC ويُهيّئك للحالات الحدّية التي تطفو دائمًا في بيئة الإنتاج.
نموذج المؤشر
حين تُعيد executeQuery() النتيجة، يكون المؤشر قبل الصف الأول. يجب استدعاء next() للانتقال إلى الصف الأول. تُعيد next() القيمة true طالما وُجد صف، وfalse حين تنتهي البيانات — وهو ما يجعلها شرطًا طبيعيًا لحلقة while:
ResultSet المفتوح يُبقي مؤشرًا على الخادم مفتوحًا، ممّا يستهلك ذاكرة على جانبَي العميل وقاعدة البيانات.
توابع الاستخراج بالنوع
يوفّر JDBC نسختين من كل تابع استخراج: واحدة تأخذ اسم العمود (سلسلة String) وأخرى تأخذ رقم العمود (عدد صحيح يبدأ من 1). أسماء الأعمدة أكثر وضوحًا وتصمد في مواجهة تغيير ترتيب الأعمدة عند استخدام SELECT *؛ أما أرقام الأعمدة فهي أسرع قليلًا في الحلقات الحرجة أداءً.
أكثر توابع الاستخراج المكتوبة استخدامًا:
getString(col)— يتوافق معVARCHARوTEXTوCHARgetLong(col)/getInt(col)— يتوافق معBIGINT/INTgetDouble(col)/getBigDecimal(col)— للأعداد العشرية والأرقام الدقيقةgetBoolean(col)— يتوافق معBOOLEAN/TINYINT(1)getTimestamp(col)— يتوافق معDATETIME/TIMESTAMPgetDate(col)— يتوافق معDATE(الجزء الزمني يُضبط على منتصف الليل)getBytes(col)— البيانات الثنائية (BLOB)getObject(col)— نوع Java الذي يختاره برنامج التشغيل؛ مفيد حين يكون النوع مجهولًا وقت الترجمة
BigDecimal للقيم المالية. يُدخل getDouble أخطاء تقريب في الفاصلة العائمة. للأعمدة المالية أو العلمية، استخدم getBigDecimal دائمًا.
التعامل مع NULL في SQL
القيمة NULL في SQL ليست مكافئة لـ null في Java. حين تستدعي تابع استخراج نوع بدائي مثل getLong() على عمود يحتوي NULL، يُعيد JDBC القيمة 0 — لا استثناء. للتمييز بين الصفر الحقيقي والقيمة الغائبة، استدعِ wasNull() مباشرة بعد الاستخراج:
أما توابع استخراج الكائنات (getString، getBigDecimal، getTimestamp) فتُعيد null بالجافا مباشرةً، لذا لا تحتاج wasNull() إلا مع توابع الأنواع البدائية.
wasNull() قبل استدعاء تابع الاستخراج. تعكس الحالة التي خلّفها آخر عمود تمّت قراءته، لا أي عمود مستقبلي. ترتيب الاستدعاءات مهم.
ResultSetMetaData: فحص الأعمدة في وقت التشغيل
يصف ResultSetMetaData شكل النتيجة — عدد الأعمدة وأسماؤها وأنواعها وقابليتها للقيم الفارغة وعروض العرض. هذا لا غنى عنه حين تكتب كودًا عامًا (مُصدِّر CSV، أداة مقارنة بيانات، طابعة بيانات اختبار) يجب أن يتعامل مع استعلامات اعتباطية:
أهم توابع ResultSetMetaData:
getColumnCount()— عدد الأعمدة في النتيجةgetColumnLabel(i)— الاسم المستعار (AS) أو الاسم الأصليgetColumnName(i)— الاسم الأصلي للعمود في الجدول (يختلف عن التسمية عند وجود اسم مستعار)getColumnTypeName(i)— نوع النوع كنص خاص بقاعدة البيانات (مثلVARCHAR)getColumnType(i)— ثابتjava.sql.Types(مثلTypes.VARCHAR == 12)isNullable(i)—columnNoNullsأوcolumnNullableأوcolumnNullableUnknowngetColumnDisplaySize(i)— الحد الأقصى لعدد الأحرف عند العرض
بناء محوّل صفوف عام
نمط شائع هو تجريد منطق تحويل النتائج إلى كائنات باستخدام واجهة دالية، وهو ما يتناسب بشكل طبيعي مع التعابير اللامبدية التي تعرفها:
RowMapper<T> وما الذي يحدث خلف الكواليس في كل طبقة تجريدية مبنية على JDBC.
ResultSets القابلة للتمرير والتعديل
الـ ResultSet افتراضيًا هو للأمام فقط وللقراءة فقط. إن احتجت للتحرك للخلف أو تعديل الصفوف في مكانها، مرّر ثوابت إضافية عند إنشاء العبارة:
أنواع التمرير: TYPE_FORWARD_ONLY (الافتراضي، الأكثر كفاءة)، TYPE_SCROLL_INSENSITIVE (لقطة ثابتة، لا ترى التغييرات المتزامنة)، TYPE_SCROLL_SENSITIVE (عرض حي، يعتمد على برنامج التشغيل ونادر الاستخدام).
UPDATE صريحة للوضوح وقابلية النقل.
الخلاصة
مؤشر الـ ResultSet يبدأ قبل الصف الأول؛ حرّكه بـ next() داخل حلقة while. استخدم توابع الاستخراج المكتوبة باسم العمود لمزيد من الوضوح، وBigDecimal للمبالغ المالية، وwasNull() بعد توابع الأنواع البدائية للتمييز بين الصفر والقيمة الغائبة. يمنحك ResultSetMetaData مخطّط الأعمدة في وقت التشغيل ويُشغّل المحوّلات العامة. تغليف منطق التحويل في واجهة دالية RowMapper<T> أسلوب نظيف وقابل للاختبار وهو أساس الأطر عالية المستوى.