Slick 3.1.x 如何实现多对多连接?

Slick 3.1.x How to implement a Many-to-Many join?

UserDao 中,我试图定义一种方法,给定用户将获得分配给它的所有安全角色。这是一个多对多的关系,这里伪数据库设计(here is the actual Tables.scala full generated implementation):

User(PK id)
SecurityRole(PK id)
UserSecurityRole(
  userId FK to User(id), 
  securityRoleId FK to SecurityRole(id)
)

因此我尝试这样定义我的 getRoles 函数:

def getRoles(user: UserRow) : Future[List[SecurityRoleRow]] = {
  val action = for {
  role <- SecurityRole join UserSecurityRole on (_.id === _.securityRoleId) 
            join User on (_.userId === _.id)
  } yield role
  db.run(action)
}

或者像这样:

def getRoles(user: UserRow) : Future[List[SecurityRoleRow]] = {
  val action = for {
    role <- SecurityRole join (UserSecurityRole join User on (_.userId === _.id)) on (_.id === _.securityRoleId)
  } yield role
  db.run(action)
}

但在这两种情况下我都得到编译器错误 can not resolve symbol _.userId 注意输出必须是 Role 因此,我用 SecurityRole 开始两个备选方案因为这就是我需要得到。我的印象是,在第一次加入后,一些列被清除了,或者?

在 SQL 中,这很简单:

SELECT t1.*
FROM SecurityRole t1, 
     UserSecurityRole t2,
     User t3
WHERE t1.id = t2.securityRoleId
  AND t3.id = t2.userId

就是这样。然后,如果我想将这个 SQL 巧妙地放入我的 UserDao 中,我可以这样做:

def getRoles(user: UserRow) : Future[List[SecurityRoleRow]] = {
  val action = sql"""SELECT t1.* " +
                    "FROM SecurityRole t1, " + 
                         "UserSecurityRole t2, " +
                    "WHERE t1.id = t2.securityRoleId " +
                      "AND ${user.id} = t2.userId""".as[SecurityRoleRow]
    db.run(action)
}

你基本上是对的(好吧,有点,它们并没有被消灭,而是元组被扩展了)。而不是这个:

def getRoles(user: UserRow) : Future[List[SecurityRoleRow]] = {
  val action = for {
  role <- SecurityRole join UserSecurityRole on (_.id === _.securityRoleId) 
            join User on (_.userId === _.id)
  } yield role
  db.run(action)
}

(顺便说一句,我相信你忘记了 result 电话)

你应该使用:

def getRoles(user: UserRow) : Future[List[SecurityRoleRow]] = {
  val action = (for {
  role <- SecurityRole join UserSecurityRole on (_.id === _.securityRoleId) 
            join User on (_._2.userId === _.id) // here is the change!!
  } yield role).result
  db.run(action)
}

每个新连接都会添加新的嵌套元组。看看我的演示文稿中的这张幻灯片(我用模式匹配解构了每个类型的元组,但它们都是一样的):http://slides.com/pdolega/slick-101#/75(尤其是 - 看看下一张幻灯片,它通过使用外键使它更整洁; 在你的情况下,这将是完美的解决方案)。

编辑:

即使没有定义外键(我认为你应该这样做),你也可以将你的版本更改为 monadic 形式,这避免了所有的元组嵌套:

val action = (for {
  role <- SecurityRole 
  userRole <- UserSecurityRole if role.id === userRole.securityRoleId 
  user <- User if userRole.userId === user.id
} yield role).result

db.run(action)