Type Projection of Foreign Keys in Scala-Slick

六月ゝ 毕业季﹏ 提交于 2019-11-30 04:37:13

问题


I'm using Scala, and new to Play and Slick. I'm starting to block out a simple database structure and I'm not sure about the correct way to handle foreign keys and projections. In the example at page bottom it currently doesn't compile because a ForeignKey cannot be lifted directly, so what's the correct way to have query results lift into my type (which is basically this sans methods and additional constructors):

case class UserCompanyPermission(pk: UUID, company: Company, user: User, accessLevel: CompanyPermissionLevel)

Either I'd like to have the type projection return a UserCompanyPermission, otherwise have a way to wrap everything coming into and out of the DAO methods so from the outside I'm just passing my Scala Types in. Basically I want to ensure my business logic is entirely decoupled from state to make it easier to test, so being able to keep all table specifics restricted to this storage package. Is their way to wrap, or is each method I write in the DAO object going to need to make the conversion on its own?

trait CompaniesComponent { this: UsersComponent =>
  val Companies: Companies
  val UserCompanyPermissions: UserCompanyPermissions

  implicit val companyPermissionLevelTypeMapper = MappedTypeMapper.base[CompanyPermissionLevel.Value, Int](
    { level => level.id }, { id => CompanyPermissionLevel(id) }
  )

  class Companies extends Table[Company]("Company") {
    def pk = column[UUID]("pk", O.PrimaryKey)
    def subdomain = column[String]("subdomain", O.NotNull)
    def name = column[String]("name", O.NotNull)

    def * = pk ~ subdomain ~ name <> (Company.apply _, Company.unapply _)
  }


  class UserCompanyPermissions extends Table[UserCompanyPermission]("UserCompanyPermission") {

    def pk = column[UUID]("pk", O.PrimaryKey)
    def company_pk = column[UUID]("company_pk", O.NotNull)
    def user_pk = column[UUID]("user_pk", O.NotNull)
    def accessLevel = column[CompanyPermissionLevel.Value]("access_level", O.NotNull)

    def company = foreignKey("company_pk", company_pk, Companies)(_.pk)
    def user = foreignKey("user_pk", user_pk, Users)(_.pk)

    def * = pk ~ company ~ user ~ accessLevel <> (UserCompanyPermission.apply _, UserCompanyPermission.unapply _)
  }

}


object Companies extends DAO {
  def insert(company: Company)(implicit session: Session) {
    Companies.insert(company)
  }
}

object UserCompanyPermissions extends DAO {
  def insert(perm: UserCompanyPermission)(implicit session: Session) {
    UserCompanyPermissions.insert(perm)
  }
}

回答1:


The recommended way to work with Slick is to never nest the case classes holding the values of your rows. They should only contain the actual columns, not any related objects, because that would hard-code that they have to be loaded together (unless you do some magical lazy loading under the hood which makes things complex for use and implementation). Instead you write queries, that associate the values for the particular data you need right now using tuples.

// FYI
case class UserCompanyPermission( pk: UUID, company_pk: UUID, user_pk: UUID, accessLevel: CompanyPermissionLevel.Value )

// associating data in an ad-hoc, not hard-coded way
for( ucp <- Query(UserCompanyPermissions).filter(_.accessLevel === LevelOne);
     c <- ucp.company;
     u <- ucp.user
) yield (u,c)

Here we load u and c because we say so. We could have only loaded u or c or c and ucp or whatever. It is not hardcoded in our row classes.

Architecture-wise, you will find help in our Scala Days 2013 talk and the Scala Exchange 2013 talk. http://slick.typesafe.com/docs/

As a side node I would recommend a sealed trait with case object children instead of Enumerationfor CompanyPermissionLevel.



来源:https://stackoverflow.com/questions/20503847/type-projection-of-foreign-keys-in-scala-slick

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!