You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Active-Slick关联关系实现示例及Active Record模式合规性咨询

嘿,很高兴你已经跑通了Active-Slick的示例项目!关于关联关系的实现,我给你梳理一下具体的方案,同时也点评下你之前的代码思路~

Active-Slick 关联关系实现指南

一、先说说你现有代码的合理性

先看你贴的这段代码片段:

val book= Book(None,"Harry Potter") 
val action = for { 
  id <- bookDao.insert(acc) 
  y<-authorDao.insert(new Author(No...

这种分步插入的方式功能上是完全可行的,但如果要严格遵循Active Record模式,其实可以调整得更贴合模式的核心思想——Active Record模式要求模型本身具备持久化能力(比如直接调用book.save()),而不是依赖单独的Dao层来执行插入操作。不过Active-Slick本身是基于Slick的Active Record风格封装,我们可以很容易调整到更贴合的写法。

二、关联关系实现示例(以Book和Author一对多为例)

假设我们的业务模型是:一个作者可以有多本书,每本书属于一个作者。下面是具体的实现步骤:

1. 定义带关联字段的模型类

首先定义模型,Book需要包含authorId字段来关联对应的Author:

// Author模型,继承ActiveRecord特质,指定主键类型为Long
case class Author(id: Option[Long], name: String) extends ActiveRecord[Long, Author]

// Book模型,通过authorId关联到Author的主键
case class Book(id: Option[Long], title: String, authorId: Long) extends ActiveRecord[Long, Book]

2. 实现ActiveRecord的数据库表映射

接下来创建对应的Table映射类,同时可以定义数据库级的外键约束(可选,但推荐用于数据一致性):

object Authors extends ActiveRecordTable[Long, Author]("authors") {
  def name = column[String]("name", O.SqlType("VARCHAR(255)"))
  
  // 定义表字段和模型的映射关系
  override def * = (id.?, name) <> (Author.tupled, Author.unapply)
}

object Books extends ActiveRecordTable[Long, Book]("books") {
  def title = column[String]("title", O.SqlType("VARCHAR(255)"))
  def authorId = column[Long]("author_id")
  
  // 定义外键关联,指定删除作者时级联删除其书籍
  def author = foreignKey("fk_book_author", authorId, Authors)(_.id.get, onDelete = ForeignKeyAction.Cascade)
  
  override def * = (id.?, title, authorId) <> (Book.tupled, Book.unapply)
}

3. 贴合Active Record模式的关联操作

(1)保存关联数据(先存作者,再存书籍)

用Active Record的风格,直接调用模型自身的insert方法,而不是依赖外部Dao:

// 创建未持久化的作者和书籍实例
val author = Author(None, "J.K. Rowling")
val book = Book(None, "Harry Potter and the Philosopher's Stone", authorId = 0) // 先占位,后续替换

// 组合数据库操作:先插入作者,再用返回的作者ID更新书籍后插入
val saveAction = for {
  savedAuthor <- author.insert // 插入后返回带生成ID的作者实例
  savedBook <- book.copy(authorId = savedAuthor.id.get).insert // 替换authorId后插入书籍
} yield (savedAuthor, savedBook)

// 执行异步操作(这里用await是示例,实际项目推荐用flatMap处理异步结果)
val (savedAuthor, savedBook) = db.run(saveAction).await

这种写法更贴合Active Record模式的核心,模型自己负责处理持久化,不需要额外的Dao层。

(2)查询关联数据(获取某作者的所有书籍)

我们可以给Author模型添加一个方法,直接查询其关联的书籍,让模型具备关联查询的能力:

case class Author(id: Option[Long], name: String) extends ActiveRecord[Long, Author] {
  // 获取当前作者的所有书籍
  def books: DBIO[Seq[Book]] = {
    Books.filter(_.authorId === id.get).result
  }
}

// 使用示例:查询指定作者及其所有书籍
val authorWithBooks = db.run(for {
  author <- Authors.filter(_.name === "J.K. Rowling").result.head
  books <- author.books
} yield (author, books)).await

三、对你原有代码的总结

你之前用Dao层分别插入Book和Author的方式功能上没问题,但如果要严格遵循Active Record模式,建议把持久化逻辑放到模型本身(利用Active-Slick提供的ActiveRecord特质自带的insert/update/delete等方法),这样更符合模式的设计思想。

另外要注意:插入操作返回的实例会包含数据库生成的主键ID,一定要用这个返回的ID来设置关联字段,避免手动硬编码或占位符导致的错误哦~

内容的提问来源于stack exchange,提问作者priyanka goel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:06:42