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)
在 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)