Я использую play 2.3 с slick 2.1.
У меня есть два связанных объекта — Message
и User
(упрощенный пример домена). Сообщения пишут пользователи. Рекомендуемый способ (единственный способ?) выражения такого отношения — использование явного 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
}
}
Теперь все это идеально внутренне, но если я хочу отобразить фактическое сообщение, я хочу, чтобы класс сообщения мог возвращать свое имя автора при использовании в шаблонах.
Вот мои соображения:
- Я не хочу (и я в любом случае не мог) обходить неявное пятно
Session
там, где ему не место - например, шаблонный движок и классы моделей. - Я хотел бы избежать запроса дополнительных данных для сообщений один за другим в этом конкретном случае
Я больше знаком с Hibernate, чем со Slick; в Hibernate я бы использовал выборку соединения. С 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) }
а потом передать куда угодно. Меня беспокоит этот дополнительный класс - не очень DRY, поэтому ненужное преобразование (и, кажется, я не могу сделать его неявным), много шаблонов.
Каков общий подход? Есть ли способ сократить дополнительные классы и действительно сделать модель способной возвращать свои ассоциации?
Hibernate
— это ORM, аSlick
— это слой, который позволяет вам писать безопасные запросы, вы не можете ожидать тех же функций, и в этом случае, в частности, вы не можете ожидать, что Slick будет извлекать объекты для вас, если вы не укажете, что в запросе с использованием соединений. Если вам больше нравятся ORM, взгляните, например, на SQueryl. - person Ende Neu   schedule 06.07.2014