2014-12-11 10 views
5

Haben solche Modelle (vereinfacht):Scala Typinferenz mit Slick Tabelle arbeiten

case class User(id:Int,name:String) 
case class Address(id:Int,name:String) 
... 

Slick (2.1.0 Version) Tabellenzuordnung:

class Users(_tableTag: Tag) extends Table[User](_tableTag, "users") with WithId[Users, User] {` 
    val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey) 
    ... 
} 
trait WithId[T, R] { 
    this: Table[R] => 
    def id: Column[Int] 
} 

Merkmal Misch WithId ich generische DAO implementieren möchten Methoden für verschiedene Tabellen mit Spalte id: Column[Int] (ich möchte, dass die Methode findById sowohl mit User als auch Address Tabellenzuordnungen funktioniert)

trait GenericSlickDAO[T <: WithId[T, R], R] { 
    def db: Database 

    def findById(id: Int)(implicit stk: SlickTableQuery[T]): Option[R] = db.withSession { implicit session => 
    stk.tableQuery.filter(_.id === id).list.headOption 
    } 

trait SlickTableQuery[T] { 
    def tableQuery: TableQuery[T] 
} 

object SlickTableQuery { 
    implicit val usersQ = new SlickTableQuery[Users] { 
    val tableQuery: Table Query[Users] = Users 
    } 
} 

Das Problem ist, dass findById nicht nicht kompiliert:

Error:(13, 45) type mismatch; found : Option[T#TableElementType] required: Option[R] stk.tableQuery.filter(_.id === id).list.headOption

Wie ich es sehe T vom Typ WithId[T, R] und zugleich vom Typ Table[R]. Slick implementiert den Typ Table so, dass wenn X=Table[Y] dann X#TableElementType=Y.

Also in meinem Fall T#TableElementType=R und Option[T#TableElementType] sollte als Option[R] abgeleitet werden, aber es ist nicht. Wo liege ich falsch?

Antwort

1

Ihre Annahme über WithId[T, R] vom Typ Table[R] ist falsch. Die Annotation vom Typ selbst in WithId[T, R] erfordert nur das Einmischen einer Table[R], was jedoch nicht bedeutet, dass WithId[T, R] eine Table[R] ist.

Ich glaube, Sie verwechseln die Erklärung von WithId mit Instanzen von WithId, die schließlich eine Instanz von Table sein müssen.

Ihr oberer Typ gebunden Einschränkung in der GenericSlickDAO Eigenschaft garantiert auch nicht die Eigenschaft WithId, eine Instanz von Table zu sein, da jeder Typ ein Subtyp von sich selbst ist.

Siehe this Frage für eine ausführlichere Erklärung über die Unterschiede zwischen Selbst-Typen und Subtypen.

1

Ich benutze Play-Slick und ich habe versucht, genau wie Sie, mit einem Merkmal und self-type ohne Erfolg zu tun.

Aber ich konnte mit den folgenden:

import modelsunscanned.TableWithId 

import scala.slick.jdbc.JdbcBackend 
import scala.slick.lifted.TableQuery 
import play.api.db.slick.Config.driver.simple._ 


/** 
* @author Sebastien Lorber ([email protected]) 
*/ 
package object models { 

    private[models] val Users = TableQuery(new UserTable(_)) 
    private[models] val Profiles = TableQuery(new ProfileTable(_)) 
    private[models] val Companies = TableQuery(new CompanyTable(_)) 
    private[models] val Contacts = TableQuery(new ContactTable(_)) 


    trait ModelWithId { 
    val id: String 
    } 


    trait BaseRepository[T <: ModelWithId] { 
    def tableQuery: TableQuery[TableWithId[T]] 


    private val FindByIdQuery = Compiled { id: Column[String] => 
     tableQuery.filter(_.id === id) 
    } 


    def insert(t: T)(implicit session: JdbcBackend#Session) = { 
     tableQuery.insert(t) 
    } 


    def getById(id: String)(implicit session: JdbcBackend#Session): T = FindByIdQuery(id).run.headOption 
     .getOrElse(throw new RuntimeException(s"Could not find entity with id=$id")) 

    def findById(id: String)(implicit session: JdbcBackend#Session): Option[T] = FindByIdQuery(id).run.headOption 




    def update(t: T)(implicit session: JdbcBackend#Session): Unit = { 
     val nbUpdated = tableQuery.filter(_.id === t.id).update(t) 
     require(nbUpdated == 1,s"Exactly one should have been updated, not $nbUpdated") 
    } 

    def delete(t: T)(implicit session: JdbcBackend#Session) = { 
     val nbDeleted = tableQuery.filter(_.id === t.id).delete 
     require(nbDeleted == 1,s"Exactly one should have been deleted, not $nbDeleted") 
    } 

    def getAll(implicit session: JdbcBackend#Session): List[T] = tableQuery.list 

    } 

} 


// play-slick bug, see https://github.com/playframework/play-slick/issues/227 
package modelsunscanned { 
    abstract class TableWithId[T](tableTag: Tag,tableName: String) extends Table[T](tableTag,tableName) { 
    def id: Column[String] 
    } 
} 

Ich gebe Ihnen eine exemple Nutzung:

object CompanyRepository extends BaseRepository[Company] { 
    // Don't know yet how to avoid that cast :(
    def tableQuery = Companies.asInstanceOf[TableQuery[TableWithId[Company]]] 

    // Other methods here 
    ... 
} 




case class Company(
        id: String = java.util.UUID.randomUUID().toString, 
        name: String, 
        mainContactId: String, 
        logoUrl: Option[String], 
        activityDescription: Option[String], 
        context: Option[String], 
        employeesCount: Option[Int] 
        ) extends ModelWithId 


class CompanyTable(tag: Tag) extends TableWithId[Company](tag,"COMPANY") { 
    override def id = column[String]("id", O.PrimaryKey) 
    def name = column[String]("name", O.NotNull) 
    def mainContactId = column[String]("main_contact_id", O.NotNull) 
    def logoUrl = column[Option[String]]("logo_url", O.Nullable) 
    def activityDescription = column[Option[String]]("description", O.Nullable) 
    def context = column[Option[String]]("context", O.Nullable) 
    def employeesCount = column[Option[Int]]("employees_count", O.Nullable) 
    // 
    def * = (id, name, mainContactId,logoUrl, activityDescription, context, employeesCount) <> (Company.tupled,Company.unapply) 
    // 
    def name_index = index("idx_name", name, unique = true) 
} 

Beachten Sie, dass active-slick ist auch mit etwas ähnliches