تكامل قواعد البيانات باستخدام Slick/Doobie
تعلّم دمج قواعد البيانات في تطبيق Play الخاص بكم باستخدام مكتبات Scala الشائعة أو أطر ORM مثل Slick أو Doobie
تكامل قواعد البيانات باستخدام Slick/Doobie درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 3 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Scala for Backend Engineering & Functional Programming، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Scala for Backend Engineering & Functional Programming 3 دروس في المجموع.
لماذا ندمج قواعد البيانات؟
غالبًا ما تحتاج تطبيقات الويب إلى تخزين البيانات واسترجاعها بشكل دائم. وقد تكون هذه البيانات ملفات تعريف المستخدمين أو معلومات المنتجات أو منشورات المدونات.
- الاستمرارية: تظل البيانات متاحة حتى بعد إعادة تشغيل التطبيق.
- قابلية التوسع: التعامل مع كميات كبيرة من البيانات والعديد من المستخدمين المتزامنين.
- الموثوقية: ضمان سلامة البيانات وإمكانية استردادها.
يُعد دمج قاعدة بيانات أمرًا ضروريًا لأي تطبيق ويب ديناميكي مبني باستخدام Play Framework.
Slick وDoobie: أدوات Scala لقواعد البيانات
توفّر Scala مكتبات قوية للتفاعل مع قواعد البيانات، ويُعد كل من Slick وDoobie خيارين شائعين.
- Slick: مكتبة للربط العلاقي الوظيفي (FRM). تتيح لكم التعامل مع قواعد البيانات بطريقة وظيفية وآمنة من حيث الأنواع، مع إخفاء تفاصيل SQL.
- Doobie: طبقة JDBC وظيفية خالصة. تمنحكم تحكمًا مباشرًا في استعلامات SQL مع التكامل مع نماذج البرمجة الوظيفية مثل Cats Effect.
سنركّز في هذا الدرس على Slick بسبب تجريده الأعلى مستوى، ما يبسّط عادةً عملية التكامل الأولية مع Play Framework.
إعداد Slick في Play
أضيفوا أولًا Slick وبرنامج تشغيل قاعدة بيانات إلى ملف build.sbt. سنستخدم H2، وهي قاعدة بيانات داخل الذاكرة، لتسهيل الإعداد.
بعد ذلك، اضبطوا اتصال قاعدة البيانات في conf/application.conf.
import play.sbt.PlayImport._
libraryDependencies ++= Seq(
jdbc,
ws,
"com.typesafe.play" %% "play-slick" % "5.0.0",
"com.typesafe.play" %% "play-slick-evolutions" % "5.0.0",
"com.h2database" % "h2" % "2.2.224"
)إعداد قاعدة البيانات
أضيفوا هذه الأسطر إلى conf/application.conf لإعداد قاعدة بيانات H2 وملف تعريف Slick. يحدّد slick.dbs.default.profile نوع قاعدة البيانات.
يعرّف play.db.prototype.slick قالبًا لاتصالات قاعدة البيانات.
slick.dbs.default.profile="slick.jdbc.H2Profile$"
play.db {
prototype.slick = {
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:play;DB_CLOSE_DELAY=-1"
db.username="sa"
db.password=""
}
# Apply the prototype to our default DB
default = ${play.db.prototype.slick}
}تعريف النموذج الخاص بكم (جدول Slick)
في Slick، تمثّلون جداول قاعدة البيانات كفئات Scala تمدّد slick.jdbc.H2Profile.api.Table. ويُربط كل عمود بنوع من أنواع Scala.
نعرّف TableQuery لكل جدول، وهو نقطة الدخول لإجراء الاستعلامات.
import slick.jdbc.H2Profile.api._
// Define a case class for our data model
case class User(id: Option[Long], name: String, email: String)
// Define the table mapping
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
// Every table needs a * projection with the default mapping
def * = (id, name, email) <> (User.tupled, User.unapply)
}
// TableQuery for accessing the Users table
val users = TableQuery[Users]الشفرة: إنشاء الجدول والمخطط
يوضّح هذا المثال القابل للتشغيل كيفية تعريف جدول User ثم إنشاء مخططه في قاعدة بيانات H2 داخل الذاكرة. تنفّذ الطريقة db.run إجراءات قاعدة البيانات بشكل غير متزامن.
import slick.jdbc.H2Profile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
case class User(id: Option[Long], name: String, email: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
def * = (id, name, email) <> (User.tupled, User.unapply)
}
object SlickDemo {
val users = TableQuery[Users]
def main(args: Array[String]): Unit = {
val db = Database.forURL("jdbc:h2:mem:test1;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
try {
val setup = DBIO.seq(
users.schema.create
)
val future = db.run(setup)
Await.result(future, 2.seconds)
println("Users table created successfully!")
} finally {
db.close()
}
}
}نمط DAO للعمليات
يساعد نمط كائن الوصول إلى البيانات (DAO) على تغليف عمليات قاعدة البيانات. وهو يفصل منطق الأعمال في تطبيقكم عن منطق الاستمرارية الخاص به.
سيحتوي UserDAO على طرق لإدراج كائنات User والاستعلام عنها وتحديثها وحذفها.
import slick.jdbc.H2Profile.api._
import scala.concurrent.Future
class UserDAO(db: Database) {
import SlickDemo.users // Assuming users TableQuery is in SlickDemo object
def insert(user: User): Future[User] = {
db.run(users returning users.map(_.id) into ((user, id) => user.copy(id = id)) += user)
}
def findAll(): Future[Seq[User]] = {
db.run(users.result)
}
def findById(id: Long): Future[Option[User]] = {
db.run(users.filter(_.id === id).result.headOption)
}
}الشفرة: إدراج البيانات والاستعلام عنها
إليكم كيفية إدراج مستخدم جديد ثم الاستعلام عن جميع المستخدمين من قاعدة البيانات باستخدام طرق DAO التي عرّفناها للتو.
import slick.jdbc.H2Profile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
case class User(id: Option[Long], name: String, email: String)
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def email = column[String]("email")
def * = (id, name, email) <> (User.tupled, User.unapply)
}
class UserDAO(db: Database) {
val users = TableQuery[Users]
def insert(user: User): Future[User] =
db.run(users returning users.map(_.id) into ((u, id) => u.copy(id = id)) += user)
def findAll(): Future[Seq[User]] =
db.run(users.result)
}
object SlickOperations {
def main(args: Array[String]): Unit = {
val db = Database.forURL("jdbc:h2:mem:test2;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
val userDAO = new UserDAO(db)
try {
Await.result(db.run(userDAO.users.schema.create), 2.seconds)
println("Table created.")
val future = for {
user1 <- userDAO.insert(User(None, "Alice", "alice@example.com"))
user2 <- userDAO.insert(User(None, "Bob", "bob@example.com"))
allUsers <- userDAO.findAll()
} yield allUsers
val result = Await.result(future, 5.seconds)
println(s"All users: $result")
} finally {
db.close()
}
}
}دمج DAO في Play
في تطبيق Play، تحقنون عادةً UserDAO في وحدة تحكم باستخدام حقن التبعية. ويتيح ذلك لإجراءات وحدة التحكم التفاعل مع قاعدة البيانات.
يمكنكم إعداد DI في Play (مثل Guice) لتوفير مثيل من Database وUserDAO.
import play.api.mvc._
import play.api.libs.json._
import scala.concurrent.ExecutionContext
import javax.inject._
// Assuming User and UserDAO are defined as before
@Singleton
class UserController @Inject()(cc: ControllerComponents, userDAO: UserDAO)
(implicit ec: ExecutionContext) extends AbstractController(cc) {
def listUsers = Action.async {
userDAO.findAll().map { users =>
Ok(Json.toJson(users))
}
}
def createUser = Action.async(parse.json) {
request =>
request.body.validate[User].map { user =>
userDAO.insert(user).map(u => Created(Json.toJson(u)))
}.getOrElse(Future.successful(BadRequest("Invalid User Data")))
}
}اختبار سريع: جدول Slick
تأمّلوا فئة الحالة التالية في Scala وتعيين Table جزئيًا في Slick:
case class Product(id: Option[Int], name: String, price: Double)أي سطر يُكمل إسقاط * لجدول Product بشكل صحيح، بافتراض تعريف أعمدة id وname وprice؟
مراجعة: دمج قواعد البيانات
استكشفنا في هذا الدرس كيفية دمج قواعد البيانات في تطبيقات Play Framework باستخدام Slick.
- تعرّفنا إلى دور Slick كمكتبة للربط العلاقي الوظيفي (FRM).
- تعلّمنا إعداد تبعيات Slick وتهيئة قاعدة البيانات في
application.conf. - رأيتم كيفية تعريف فئات الحالة في Scala وربطها بجداول قاعدة البيانات باستخدام فئة
Tableفي Slick. - قدّمنا نمط DAO لتنظيم عمليات قاعدة البيانات، مع أمثلة على إدراج البيانات والاستعلام عنها.
- وأخيرًا، تطرّقنا إلى دمج عمليات DAO هذه في وحدات تحكم Play لنقاط نهاية واجهة Web API.
يُعد إتقان دمج قواعد البيانات أمرًا أساسيًا لبناء تطبيقات Play قوية تعتمد على البيانات!
الأسئلة الشائعة
هل درس «تكامل قواعد البيانات باستخدام Slick/Doobie» مجاني؟
نعم — نص درس «تكامل قواعد البيانات باستخدام Slick/Doobie» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 3 دروس في المجموع.
ماذا ستتعلم في «تكامل قواعد البيانات باستخدام Slick/Doobie»؟
تعلّم دمج قواعد البيانات في تطبيق Play الخاص بكم باستخدام مكتبات Scala الشائعة أو أطر ORM مثل Slick أو Doobie تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Scala for Backend Engineering & Functional Programming؟
لا تُشترط خبرة سابقة. Scala for Backend Engineering & Functional Programming على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 3.
كم من الوقت يستغرق درس «تكامل قواعد البيانات باستخدام Slick/Doobie»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Scala for Backend Engineering & Functional Programming هذا؟
نعم. كل درس في Scala for Backend Engineering & Functional Programming يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أساسيات Play Framework
- بناء RESTful APIs باستخدام Play
- تكامل قواعد البيانات باستخدام Slick/Doobie