Scala 使用slick 2加载相关实体的策略
我正在使用play 2.3和slick 2.1 我有两个相关的实体-Scala 使用slick 2加载相关实体的策略,scala,playframework-2.0,slick-2.0,Scala,Playframework 2.0,Slick 2.0,我正在使用play 2.3和slick 2.1 我有两个相关的实体-消息和用户(一个简化的示例域)。消息由用户编写。 表达这种关系的方法是在消息中使用显式userId 我的类和表映射如下所示: case class Message ( text: String, userId: Int, date: Timestamp = new Timestamp(new Date().getTime()), id: Option[Int] = None) {} case c
消息
和用户
(一个简化的示例域)。消息由用户编写。
表达这种关系的方法是在消息中使用显式userId
我的类和表映射如下所示:
case class Message (
text: String,
userId: Int,
date: Timestamp = new Timestamp(new Date().getTime()),
id: Option[Int] = None) {}
case class User (
userName: String,
displayName: String,
passHash: String,
creationDate: Timestamp = new Timestamp(new Date().getTime()),
lastVisitDate: Option[Timestamp] = None,
// etc
id: Option[Int] = None){}
class MessageTable(tag: Tag) extends Table[Message](tag, "messages") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def text = column[String]("text")
def userId = column[Int]("user_id")
def date = column[Timestamp]("creation_date")
def * = (title, text, userId, date, id.?) <> (Post.tupled, Post.unapply
def author = foreignKey("message_user_fk", userId, TableQuery[UserTable])(_.id)
}
class UserTable(tag: Tag) extends Table[User](tag, "USER") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def username = column[String]("username")
def passHash = column[String]("password")
def displayname = column[String]("display_name")
def * = (username, passHash,created, lastvisit, ..., id.?) <> (User.tupled, User.unapply)
}
val messageList = (
for (
u <- db.users;
m <- db.messages if u.id === m.userId
) yield (u.displayName, m.date, m.text))
.sortBy({ case (name, date, text) => date })
.drop(n)
.take(amount)
.list()
@(messagesAndAuthors: List[(User, Message)])
现在,这在内部是完美的,但是如果我想显示实际的消息,我希望消息类在模板中使用时能够返回它的作者名称
以下是我的考虑:
我不想(无论如何我也不能)在它不属于的地方传递隐式的slickSession
,比如模板引擎和模型类
在这种特殊情况下,我希望避免逐个请求消息的附加数据
我对Hibernate比对Slick更熟悉;在Hibernate中,我会使用join抓取。有了Slick,我想到的最好办法是使用另一个data holder类进行显示:
class LoadedMessage (user:User, message:Message) {
// getters
def text = message.text
def date = message.date
def author = user.displayName
}
object LoadedMessage {
def apply( u:User , m:Message ) = new LoadedMessage(u, m)
}
并用联接查询的结果填充它:
val messageList: List[LoadedMessage] = (
for (
u <- db.users;
m <- db.messages if u.id === m.userId
) yield (u, m))
.sortBy({ case (u, m) => m.date })
.drop(n)
.take(amount)
.list.map { case (u, m) => LoadedMessage(u, m) }
val messageList:List[LoadedMessage]=(
为了(
u LoadedMessage(u,m)}
然后把它传递到任何地方。我关心的是这个额外的类-不是很枯燥,所以不必要的转换(而且似乎我不能使它隐式),很多样板
常用的方法是什么?
有没有办法减少额外的类并使模型能够返回其关联性?以下是我的评论:
如何处理连接结果在我看来是个人喜好的问题,我也会使用您的方法,您有一个特殊的数据结构,它封装了您的数据,并且可以轻松地传递(例如在视图中)和访问
我想到的另外两种方法是
查询字段而不是对象,它不太清晰,我通常不喜欢使用元组(在本例中是三元组),因为我发现符号。\u 1
比MyClass.myField
更不清晰。这意味着这样做:
case class Message (
text: String,
userId: Int,
date: Timestamp = new Timestamp(new Date().getTime()),
id: Option[Int] = None) {}
case class User (
userName: String,
displayName: String,
passHash: String,
creationDate: Timestamp = new Timestamp(new Date().getTime()),
lastVisitDate: Option[Timestamp] = None,
// etc
id: Option[Int] = None){}
class MessageTable(tag: Tag) extends Table[Message](tag, "messages") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def text = column[String]("text")
def userId = column[Int]("user_id")
def date = column[Timestamp]("creation_date")
def * = (title, text, userId, date, id.?) <> (Post.tupled, Post.unapply
def author = foreignKey("message_user_fk", userId, TableQuery[UserTable])(_.id)
}
class UserTable(tag: Tag) extends Table[User](tag, "USER") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def username = column[String]("username")
def passHash = column[String]("password")
def displayname = column[String]("display_name")
def * = (username, passHash,created, lastvisit, ..., id.?) <> (User.tupled, User.unapply)
}
val messageList = (
for (
u <- db.users;
m <- db.messages if u.id === m.userId
) yield (u.displayName, m.date, m.text))
.sortBy({ case (name, date, text) => date })
.drop(n)
.take(amount)
.list()
@(messagesAndAuthors: List[(User, Message)])
在这里,您可以在视图中传递如下内容:
case class Message (
text: String,
userId: Int,
date: Timestamp = new Timestamp(new Date().getTime()),
id: Option[Int] = None) {}
case class User (
userName: String,
displayName: String,
passHash: String,
creationDate: Timestamp = new Timestamp(new Date().getTime()),
lastVisitDate: Option[Timestamp] = None,
// etc
id: Option[Int] = None){}
class MessageTable(tag: Tag) extends Table[Message](tag, "messages") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def text = column[String]("text")
def userId = column[Int]("user_id")
def date = column[Timestamp]("creation_date")
def * = (title, text, userId, date, id.?) <> (Post.tupled, Post.unapply
def author = foreignKey("message_user_fk", userId, TableQuery[UserTable])(_.id)
}
class UserTable(tag: Tag) extends Table[User](tag, "USER") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def username = column[String]("username")
def passHash = column[String]("password")
def displayname = column[String]("display_name")
def * = (username, passHash,created, lastvisit, ..., id.?) <> (User.tupled, User.unapply)
}
val messageList = (
for (
u <- db.users;
m <- db.messages if u.id === m.userId
) yield (u.displayName, m.date, m.text))
.sortBy({ case (name, date, text) => date })
.drop(n)
.take(amount)
.list()
@(messagesAndAuthors: List[(User, Message)])
然后使用元组和类访问功能访问数据,但您的模板仍然是\u 1
的集合,而且读取起来很糟糕,而且您必须执行类似消息和作者的操作。_1.name
仅获取一个值
最后,我更喜欢在我的观点中尽可能干净地传递变量(我猜这是计算机科学中为数不多的普遍接受的原则之一)隐藏模型中使用的逻辑,因为在我看来,使用一个特别的case类是一个很好的方法。case类是一个非常强大的工具,拥有它很好,对你来说,它可能看起来不枯燥,比其他方法更冗长(比如你提到的Hibernate
)但是想想看,当一个开发人员阅读你的代码时,这将是最简单的,因为它可以在这一刻得到,额外的小类将节省几个小时的头痛
对于会话
问题,目前无法避免传递它(据我所知),但您应该能够控制这种情况并避免在模板中传递会话,我通常在我的入口点(大部分时间是控制器)创建一个会话并将其传递给模型
注意:代码未经测试,可能存在一些错误。仅需考虑一点,Hibernate
是一个ORM,而Slick
是一个允许您编写类型安全查询的层,您不能期望相同的功能,在这种情况下,您尤其不能期望Slick为您获取对象,除非您在q中指定uery使用联接。如果您更喜欢ORM,请看一看示例。我并不讨厌编写查询或联接表。我更不知道如何用高效的方式表示结果。谢谢技巧,我也会看一看Squeryl。我只是开始玩Play,几乎整个堆栈都不熟悉。我使用Play slick来o、 因此,控制器中的隐式会话
并没有太大问题。我最初的尝试是实现延迟加载关联,但我在视图中传递隐式会话
方面并不是很成功。现在回想起来,这是一个可怕的想法。我明白,使用play和slick,在将其传递给用户之前,必须加载所有内容关于你的第二种方法,我还可以考虑用author(u:User,m:Message){}创建一个简单的case类MessageWithAuthor(u:User,m:Message){}
虽然代码较少,但访问它却不那么漂亮。虽然仍然比元组好,但我担心它可能会以地狱般的MessageWithAuthor
,MessageWithResponses
,MessageWithParent
等结尾。或者可能会使用相关的选项制作messagewithallrelatedstup
,但恩,我会丢失编译时检查我是否实际加载了我使用的东西。您可以使用一个封装User
和Message
的case类,但这不会比您以前的方法更枯燥,相反,访问会更冗长(类似于MessageWithAuthor.u.name
).如果你真的担心在每种情况下都会有一个case类,那么你应该使用元组,如果你需要3或4个case类来完成这项工作,那么最终这是一个折衷,这就是我要做的,对我来说,易读性比干燥性(到一定长度)更重要,如果你的用例有所有可能的组合,那么元组就是最好的选择。我用元组做了尝试;这并不坏,因为我可以在模板中分解它们,以便于理解和匹配。最后,将元组用于奇怪的组合,将类用于普通组合是最有意义的。