用光滑2加载相关实体的策略

时间:2014-07-06 12:30:11

标签: scala playframework-2.0 slick-2.0

我正在使用游戏2.3和光滑的2.1

我有两个相关实体 - MessageUser(简化示例域)。消息由用户编写。 表达此类关系的A recommended way (the only way?)是在userId

中使用明确的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)
}

一个方便的辅助对象:

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. 我想避免在这种特殊情况下逐个请求邮件的附加数据
  3. 我比Slick更熟悉Hibernate;在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) }
    

    然后将其传递到任何地方。我担心的是这个额外的类 - 不是很干,所以不必要的转换(似乎我不能暗示它),很多样板。

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

1 个答案:

答案 0 :(得分: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的集合,再次阅读起来很糟糕,而且您必须执行messagesAndAuthors._1.name之类的操作只是为了得到一个值。

  3. 最后,我更喜欢在我的视图中尽可能地传递变量(我猜这是计算机科学中为数不多的普遍接受的原则之一)并隐藏模型中使用的逻辑,为此使用临时案例在我看来,课程是我的选择。案例类是一个非常强大的工具,很高兴拥有它,对你而言,它可能看起来不像其他方法(比如你所谈到的Hibernate那样干涩和冗长)但是认为当开发人员会读取你的代码时它会在这一刻变得如此简单,而且额外的小班级将节省数小时的头部撞击。

    对于Session问题,请参阅this related question,目前无法避免传递它(据我所知)但你应该能够控制这种情况并避免传递会话你的模板,我通常在我的入口点创建一个会话(大多数时候是一个控制器)并将其传递给模型。

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