【发布时间】:2013-04-07 17:18:06
【问题描述】:
我试图弄清楚如何在 Slick 中添加带有映射投影的查询,但无论我尝试什么,我都会遇到以下错误:
[error] (run-main) scala.slick.SlickException:
Select(TableNode, "USER_NAME") found. This is typically
caused by an attempt to use a "raw" table object directly
in a query without introducing it through a generator.
scala.slick.SlickException: Select(TableNode, "USER_NAME")
found. This is typically caused by an attempt to use a "raw" table
object directly in a query without introducing it through a generator.
at scala.slick.ast.Select.<init>(Node.scala:340)
at scala.slick.ast.Select.copy(Node.scala:338)
at scala.slick.ast.Select.nodeRebuild(Node.scala:345)
at scala.slick.ast.Select.nodeRebuild(Node.scala:338)
at scala.slick.ast.UnaryNode$class.nodeRebuild(Node.scala:141)
at scala.slick.ast.Select.nodeRebuild(Node.scala:338)
at scala.slick.ast.SimpleNode$$anonfun$nodeMapChildren$1.apply(Node.scala:62)
at scala.slick.ast.SimpleNode$$anonfun$nodeMapChildren$1.apply(Node.scala:62)
at scala.Option.map(Option.scala:145)
at scala.slick.ast.SimpleNode$class.nodeMapChildren(Node.scala:62)
at scala.slick.ast.Select.nodeMapChildren(Node.scala:338)
at scala.slick.compiler.Inline.scala$slick$compiler$Inline$$tr$1(Inline.scala:67)
at scala.slick.compiler.Inline$$anonfun$scala$slick$compiler$Inline$$tr$1$3.apply(Inline.scala:67)
at scala.slick.compiler.Inline$$anonfun$scala$slick$compiler$Inline$$tr$1$3.apply(Inline.scala:67)
at scala.slick.ast.Node$$anonfun$nodeMapNodes$1.apply(Node.scala:38)
at scala.slick.ast.Node$$anonfun$nodeMapNodes$1.apply(Node.scala:37)
at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:47)
at scala.slick.ast.Node$class.nodeMapNodes(Node.scala:37)
at scala.slick.ast.ProductNode$$anon$3.nodeMapNodes(Node.scala:82)
at scala.slick.ast.SimpleNode$class.nodeMapChildren(Node.scala:62)
at scala.slick.ast.ProductNode$$anon$3.nodeMapChildren(Node.scala:82)
at scala.slick.compiler.Inline.scala$slick$compiler$Inline$$tr$1(Inline.scala:67)
at scala.slick.compiler.Inline$$anonfun$scala$slick$compiler$Inline$$tr$1$3.apply(Inline.scala:67)
at scala.slick.compiler.Inline$$anonfun$scala$slick$compiler$Inline$$tr$1$3.apply(Inline.scala:67)
at scala.slick.ast.Node$$anonfun$nodeMapNodes$1.apply(Node.scala:38)
at scala.slick.ast.Node$$anonfun$nodeMapNodes$1.apply(Node.scala:37)
at scala.collection.immutable.List.foreach(List.scala:309)
at scala.slick.ast.Node$class.nodeMapNodes(Node.scala:37)
at scala.slick.ast.Pure.nodeMapNodes(Node.scala:151)
at scala.slick.ast.SimpleNode$class.nodeMapChildren(Node.scala:62)
at scala.slick.ast.Pure.nodeMapChildren(Node.scala:151)
at scala.slick.compiler.Inline.scala$slick$compiler$Inline$$tr$1(Inline.scala:67)
at scala.slick.compiler.Inline$$anonfun$scala$slick$compiler$Inline$$tr$1$3.apply(Inline.scala:67)
at scala.slick.compiler.Inline$$anonfun$scala$slick$compiler$Inline$$tr$1$3.apply(Inline.scala:67)
at scala.slick.ast.Node$$anonfun$nodeMapNodes$1.apply(Node.scala:38)
at scala.slick.ast.Node$$anonfun$nodeMapNodes$1.apply(Node.scala:37)
at scala.collection.immutable.List.foreach(List.scala:309)
我是 Scala 和 Slick 的新手,很难理解消息的含义。我认为这与此有关(取自 slick docs):
请注意,Slick 会在后台克隆您的表对象,因此您不应向它们添加任何额外的状态(不过额外的方法也可以)。还要确保表的实际对象没有定义在静态位置(即在顶层或仅嵌套在其他对象内),因为这可能会在某些情况下由于 scalac 执行的过度优化而导致问题。为您的表使用 val(具有匿名结构类型或单独的类定义)在任何地方都可以。 Lifted Embedding
我已经修改了 slick-examples MultiDBCakeExample 使其看起来像下面的代码,并尝试了几种排列但没有运气。下面的代码取自我的slick-examples project 的分叉版本。这是一个指向 my changes 的链接。
package com.typesafe.slick.examples.lifted
import scala.slick.driver.ExtendedProfile
import scala.slick.session.Session
import scala.slick.driver.H2Driver
import scala.slick.driver.SQLiteDriver
trait Profile {
val profile: ExtendedProfile
}
case class Picture(url: String, id: Option[Int] = None)
trait PictureComponent { this: Profile => //requires a Profile to be mixed in...
import profile.simple._ //...to be able import profile.simple._
object Pictures extends Table[(String, Option[Int])]("PICTURES") {
// ^ Table comes from the *current* profile
def id = column[Option[Int]]("PIC_ID", O.PrimaryKey, O.AutoInc)
def url = column[String]("PIC_URL", O.NotNull)
def * = url ~ id
val autoInc = url returning id into { case (url, id) => Picture(url, id) }
def insert(picture: Picture)(implicit session: Session): Picture = {
autoInc.insert(picture.url)
}
}
}
case class User(name: String, picture: Picture, id: Option[Int] = None)
case class UserAndPicId(name: String, picId: Int)
trait UserComponent { this: Profile with PictureComponent => //requires Profile and Picture (see def insert)
import profile.simple._
object Users extends Table[(String, Int, Option[Int])]("USERS") {
def id = column[Option[Int]]("USER_ID", O.PrimaryKey, O.AutoInc)
def name = column[String]("USER_NAME", O.NotNull)
def pictureId = column[Int]("PIC_ID", O.NotNull)
def * = name ~ pictureId ~ id
def justNameAndPicId = name ~ pictureId <> (UserAndPicId, UserAndPicId.unapply _)
private def autoInc(implicit session: Session) = name ~ pictureId returning id into {
case (_, id) => id
}
def insert(user: User)(implicit session: Session): User = {
val picture = if (user.picture.id.isEmpty) { //if no picture id...
Pictures.insert(user.picture) //...insert
} else user.picture //else return current picture
val id = autoInc.insert(user.name, picture.id.get)
user.copy(picture = picture, id = id)
}
def userAndPicById = for {
id <- Parameters[Int]
user <- this if user.id === id
} yield justNameAndPicId
}
}
/**
* The Data Access Layer contains all components and a profile
*/
class DAL(override val profile: ExtendedProfile) extends UserComponent with PictureComponent with Profile {
import profile.simple._
def create(implicit session: Session): Unit = {
(Users.ddl ++ Pictures.ddl).create //helper method to create all tables
}
}
/**
* Run SLICK code with multiple DBMSs using the Cake pattern.
* Typically this technique can be used to have different DBMS
* in production and test (other scenarios are possible as well)
*/
object MultiDBCakeExample {
// We only need the DB/session imports outside the DAL
import scala.slick.session.{ Database, Session }
def run(name: String, dal: DAL, db: Database) {
import dal._
import dal.profile.simple._
println("Running test against " + name)
db withSession { implicit session: Session =>
dal.create
//creating our default picture
val defaultPic = Pictures.insert(Picture("http://pics/default"))
println(" Inserted picture: " + defaultPic)
//inserting users
val (user1, user2, user3) = (User("name1", defaultPic), User("name2", Picture("http://pics/2")), User("name3", defaultPic))
println(" Inserted user: " + Users.insert(user1))
println(" Inserted user: " + Users.insert(user2))
println(" Inserted user: " + Users.insert(user3))
println(" All pictures: " + Query(Pictures).list)
println(" All users : " + Query(Users).list)
println(" Attempting to fetch UserAndPicId by id")
Users.userAndPicById(1).firstOption
}
}
def main(args: Array[String]) {
run("H2", new DAL(H2Driver),
Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver"))
run("SQLite", new DAL(SQLiteDriver),
Database.forURL("jdbc:sqlite::memory:", driver = "org.sqlite.JDBC"))
}
}
【问题讨论】: