التعبيرية هي خاصية مثيرة للاهتمام من لغات البرمجة. من خلال الجمع بين التعبيرات ، يمكنك تحقيق نتائج رائعة. ترفض بعض اللغات فكرة التعبير عن عمد ، لكن Kotlin بالتأكيد ليست مثل هذه اللغة.
باستخدام تركيبات اللغة الأساسية وقليل من السكر ، سنحاول إعادة إنشاء SQL في بناء جملة Kotlin في أقرب وقت ممكن.

رابط GitHub للصبر
هدفنا هو مساعدة المبرمج على التقاط مجموعة فرعية محددة من الأخطاء في مرحلة التجميع. كونها لغة مكتوبة بقوة ، ستساعدنا Kotlin على التخلص من التعبيرات غير الصالحة في بنية استعلام SQL. كمكافأة ، سنحصل على المزيد من الحماية من الأخطاء المطبعية والمساعدة من IDE في كتابة الطلبات. لا يمكن إصلاح عيوب SQL تمامًا ، ولكن من الممكن تمامًا إصلاح بعض مجالات المشكلة.
ستخبرك هذه المقالة عن مكتبة Kotlin ، والتي تسمح لك بكتابة استعلامات SQL في بناء جملة Kotlin. أيضا ، نلقي نظرة على داخل المكتبة لفهم كيفية عمل ذلك.
جزء من النظرية
يشير SQL إلى لغة الاستعلام الهيكلية ، أي بنية الاستعلامات موجودة ، على الرغم من أن بناء الجملة ضعيف - تم إنشاء اللغة بحيث يمكن استخدامها من قبل أي مستخدم ليس لديه حتى مهارات البرمجة.
ومع ذلك ، تحت SQL يوجد أساس قوي إلى حد ما في شكل نظرية قواعد البيانات العلائقية - كل شيء منطقي للغاية هناك. لفهم هيكل الاستعلامات ، ننتقل إلى تحديد بسيط:
SELECT id, name
المهم أن نفهم: يتكون الطلب من ثلاثة أجزاء متتالية. يعتمد كل جزء من هذه الأجزاء ، أولاً ، على الجزء السابق ، وثانيًا ، يتضمن مجموعة محدودة من التعبيرات لمواصلة الطلب. في الواقع ، الأمر ليس كذلك تمامًا: من الواضح أن تعبير FROM هنا أساسي فيما يتعلق بـ SELECT ، لأنه تعتمد مجموعة الحقول التي يمكننا اختيارها على الجدول الذي يتم التحديد منه ، وليس العكس.

Porting to Kotlin
لذا ، يعد FROM أساسيًا فيما يتعلق بأي تركيبات لغة استعلام أخرى. من هذا التعبير تنشأ جميع الخيارات الممكنة لمواصلة الاستعلام. في Kotlin ، نعكس هذا من خلال الدالة from (T) ، والتي ستأخذ كائن إدخال ، وهو جدول يحتوي على مجموعة من الأعمدة.
object Employees : Table("employees") { val id = Column("id") val name = Column("name") val organizationId = Column("organization_id") }
ستقوم الدالة بإرجاع كائن يحتوي على طرق تعكس استمرار الطلب المحتمل. دائمًا ما تأتي من الإنشاء أولاً ، قبل أي تعبيرات أخرى ، لذا فهي تتضمن عددًا كبيرًا من الامتدادات ، بما في ذلك SELECT النهائية (على عكس SQL ، حيث تأتي SELECT دائمًا قبل FROM). سيبدو الرمز المعادل لاستعلام SQL أعلاه كما يلي:
from(Employees) .where { e -> e.organizationId eq 1 } .select { e -> e.id .. e.name }
ومن المثير للاهتمام ، بهذه الطريقة يمكننا منع SQL غير صالحة حتى في وقت الترجمة. يشير كل تعبير وكل استدعاء أسلوب في السلسلة إلى عدد محدود من الامتدادات. يمكننا التحكم في صحة الطلب باستخدام لغة Kotlin. على سبيل المثال ، لا يشير التعبير حيث إلى استمرار في شكل آخر حيث ، علاوة على ذلك ، من ، ولكن مجموعة الإنشاءات من خلال الحصول على orderBy والحد والحد والإزاحة والاختيار النهائي كلها صالحة.

تم تمرير Lambdas كحجج إلى حيث تم تصميم عبارات واختيار لبناء المسند والإسقاط ، على التوالي (ذكرناها سابقًا). يتم تمرير جدول إلى إدخال لامدا بحيث يمكنك الوصول إلى الأعمدة. من المهم الحفاظ على سلامة النوع عند هذا المستوى أيضًا - بمساعدة عامل التشغيل الزائد ، يمكننا التأكد من أن المسند سيكون في النهاية تعبيرًا زائفًا منطقيًا لا يمكن ترجمته إذا كان هناك خطأ في بناء الجملة أو خطأ متعلق بالنوع. وينطبق نفس الشيء على الإسقاط.
fun where(predicate: (T) -> Predicate): WhereClause<T> fun select(projection: (T) -> Iterable<Projection>): SelectStatement<T>
انضم
تسمح لك قواعد البيانات العلائقية بالعمل مع العديد من الجداول والعلاقات بينها. سيكون من الجيد إعطاء المطور فرصة للعمل مع JOIN في مكتبتنا. لحسن الحظ ، يتناسب النموذج العلائقي بشكل جيد مع كل ما تم وصفه سابقًا - ما عليك سوى إضافة طريقة الانضمام ، والتي ستضيف جدولًا ثانيًا إلى تعبيرنا.
fun <T2: Table> join(table2: T2): JoinClause<T, T2>
في هذه الحالة ، سيكون لدى JOIN طرق مماثلة لتلك التي يوفرها تعبير FROM ، والفرق الوحيد هو أن الإسقاط و lambdas المسند سيستغرق معلمتين لكل منهما للوصول إلى أعمدة كلا الجدولين.
from(Employees) .join(Organizations).on { e, o -> o.id eq e.organizationId } .where { e, o -> e.organizationId eq 1 } .select { e, o -> e.id .. e.name .. o.name }
إدارة البيانات
لغة معالجة البيانات هي أداة لغة SQL ، بالإضافة إلى الاستعلام عن الجداول ، تتيح لك إدراج البيانات وتعديلها وحذفها. تتناسب هذه التصاميم بشكل جيد مع نموذجنا. لدعم التحديث والحذف ، نحتاج فقط إلى تكملة التعبيرات من وأين بمتغير مع استدعاء الطرق المقابلة. لدعم الإدراج ، نقدم وظيفة إضافية.
from(Employees) .where { e -> e.id eq 1 } .update { e -> e.name("John Doe") } from(Employees) .where { e -> e.id eq 0 } .delete() into(Employees) .insert { e -> e.name("John Doe") .. e.organizationId(1) }
وصف البيانات
يعمل SQL مع البيانات المنظمة في شكل جداول. تتطلب الجداول وصفا قبل العمل معها. يسمى هذا الجزء من اللغة لغة تعريف البيانات.
يتم تنفيذ عبارات CREATE TABLE و DROP TABLE بالمثل - ستعمل الوظيفة over كنقطة البداية.
over(Employees) .create { integer(it.id).primaryKey(autoIncrement = true).. text(it.name).unique().notNull().. integer(it.organizationId).foreignKey(references = Organizations.id) }
over(Employees).drop()