ما هو Exposed ولماذا نحتاجه؟
\n\nExposed هي مكتبة ORM (تعيين الكائنات العلائقية) خفيفة الوزن ولكنها قوية للغة البرمجة Kotlin، تم تطويرها بواسطة شركة JetBrains. توفر طريقتين رئيسيتين للعمل مع قواعد البيانات: DSL (لغة خاصة بالمجال) و DAO (كائنات الوصول إلى البيانات).
\n\nلماذا نحتاج Exposed؟
\n- \n
- أمان الأنواع: على عكس استعلامات SQL الخام، يستخدم Exposed بنى Kotlin المحددة الأنواع، مما يسمح باكتشاف الأخطاء في مرحلة الترجمة. \n
- الإيجاز: الكود في Exposed أقصر بكثير وأكثر قابلية للقراءة من استعلامات SQL المكافئة أو كود Java مع JPA. \n
- الأداء: لا يستخدم Exposed الانعكاس (reflection) أو الكائنات الوكيلة (proxy) مثل Hibernate، مما يجعله سريعًا جدًا وقابلًا للتنبؤ. \n
- المرونة: يمكنك التبديل بسهولة بين DSL (للاستعلامات المعقدة) و DAO (لعمليات CRUD البسيطة) ضمن مشروع واحد. \n
- دعم قواعد بيانات متعددة: PostgreSQL, MySQL, MariaDB, SQLite, H2, Oracle, SQL Server. \n
تثبيت Exposed
\n\nيتم تثبيت Exposed عبر نظام البناء Gradle أو Maven. أضف التبعيات التالية إلى ملف build.gradle.kts الخاص بك:
dependencies {\n // المكتبة الأساسية\n implementation("org.jetbrains.exposed:exposed-core:0.44.1")\n // DSL للاستعلامات\n implementation("org.jetbrains.exposed:exposed-dao:0.44.1")\n // مشغل JDBC لقاعدة البيانات الخاصة بك (مثال لـ PostgreSQL)\n implementation("org.postgresql:postgresql:42.7.1")\n // تجمع اتصالات HikariCP (موصى به)\n implementation("com.zaxxer:HikariCP:5.1.0")\n // تسجيل SQL (اختياري)\n implementation("ch.qos.logback:logback-classic:1.4.14")\n}\n\nلـ Maven (pom.xml):
<dependency>\n <groupId>org.jetbrains.exposed</groupId>\n <artifactId>exposed-core</artifactId>\n <version>0.44.1</version>\n</dependency>\n\nبداية سريعة: مثال كود
\n\nلننشئ تطبيقًا بسيطًا لإدارة المهام (ToDo):
\n\nimport org.jetbrains.exposed.sql.*\nimport org.jetbrains.exposed.sql.transactions.transaction\nimport org.jetbrains.exposed.sql.SqlExpressionBuilder.eq\nimport java.time.LocalDateTime\n\n// 1. تعريف الجدول\nobject Tasks : Table() {\n val id = integer("id").autoIncrement()\n val title = varchar("title", 255)\n val description = text("description").nullable()\n val isCompleted = bool("is_completed").default(false)\n val createdAt = datetime("created_at").default(LocalDateTime.now())\n\n override val primaryKey = PrimaryKey(id)\n}\n\n// 2. Data class للراحة\ndata class Task(\n val id: Int,\n val title: String,\n val description: String?,\n val isCompleted: Boolean,\n val createdAt: LocalDateTime\n)\n\nfun main() {\n // 3. الاتصال بقاعدة البيانات (H2 في الذاكرة كمثال)\n Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")\n\n transaction {\n // 4. إنشاء الجدول\n SchemaUtils.create(Tasks)\n\n // 5. إدراج البيانات\n val insertId = Tasks.insert {\n it[title] = "دراسة Exposed"\n it[description] = "قراءة التوثيق وكتابة مثال"\n } get Tasks.id\n\n println("تم إدراج مهمة بالمعرف: $insertId")\n\n // 6. اختيار جميع المهام\n val allTasks = Tasks.selectAll().map { row ->\n Task(\n id = row[Tasks.id],\n title = row[Tasks.title],\n description = row[Tasks.description],\n isCompleted = row[Tasks.isCompleted],\n createdAt = row[Tasks.createdAt]\n )\n }\n println("جميع المهام: $allTasks")\n\n // 7. تحديث مهمة\n Tasks.updat