0

我正在使用带有 slick 2.1 的 play 2.3

我有两个相关的实体 -MessageUser(一个简化的示例域)。消息由用户编写。 表达这种关系的推荐方式(唯一的方式?)是使用显userIdMessage

我的类和表映射如下所示:

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

还有一个方便的辅助对象:

object db {
    object users extends TableQuery(new UserTable(_)) {
        // helper methods, compiled queries
    }
    object messages extends TableQuery(new MessageTable(_)) {
        // helper methods, compiled queries
    }
}

现在,这在内部是完美的,但是如果我想显示实际的消息,我希望消息类在模板中使用时能够返回它的作者姓名。

以下是我的考虑:

  1. 我不想(而且我无论如何也不能)Session在它不属于的地方传递隐式光滑 - 比如模板引擎和模型类
  2. 在这种特殊情况下,我想避免一一请求消息的附加数据

我对 Hibernate 比对 Slick 更熟悉;在 Hibernate 中,我会使用 join-fetching。对于 Slick,我想出的最佳想法是使用另一个数据保持器类进行显示:

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

然后将它传递到任何地方。我关心的是这个额外的类——不是很干,所以不必要的转换(而且似乎我不能让它隐含),很多样板。

常见的做法是什么?有没有办法减少额外的类并实际上使模型能够返回它的关联?

4

1 回答 1

0

按照我的评论:

在我看来,如何处理连接结果是个人喜好问题,你的方法也是我会使用的,你有一个特殊的数据结构来封装你的数据,并且可以很容易地传递(例如在视图中)和访问。

想到的另外两种方法是

  1. 查询字段而不是对象,它不太易读,我通常讨厌使用元组(在这种情况下是三元组),因为我发现这种表示法_._1MyClass.myField. 这意味着做这样的事情:

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

    这将返回一个三元组,然后可以在您的视图中传递,这是可能的,但我永远不会这样做。

  2. 另一种选择是传递对象的元组,与您的方法非常相似,除了最后一个map

    val messageList: List[(User, Message)] = (
      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()
    

    在这里,您可以在视图中传递如下内容:

    @(messagesAndAuthors: List[(User, Message)])
    

    然后使用元组和类访问功能访问数据,但是您的模板_1又将是 s 的集合,并且再次阅读起来很糟糕,而且您必须做一些事情,例如messagesAndAuthors._1.name只获得一个值。

最后,我更喜欢在我的观点中尽可能干净地传递变量(我想这是计算机科学中为数不多的普遍接受的原则之一)并隐藏模型中使用的逻辑,因为使用临时案例类是在我看来要走的路。案例类是一个非常强大的工具,很高兴拥有它,对您来说,它可能看起来并不枯燥并且比其他方法(就像Hibernate您谈到的那样)更冗长,但认为当开发人员阅读您的代码时,它会像它一样简单可以在这一刻进入,额外的小班课程将节省数小时的头部撞击。

对于Session麻烦看到这个相关的问题,目前无法避免传递它(据我所知)但你应该能够包含这种情况并避免在你的模板中传递会话,我通常在我的入口点(大多数时候是控制器)并将其传递给模型。

注意:代码未经测试,可能存在一些错误。

于 2014-07-06T13:38:02.570 回答