Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

slick 2.0 define generic `find by field` method

import scala.slick.driver.MySQLDriver.simple._

class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {

  case class QueryExt[B](q: Query[RichTable.this.type, B])  {
    def whereEq[C](col: RichTable.this.type => Column[C], c: C) = {
      q.filter { fields =>
        col(fields) === c
      }
    }
  }

}

Then it complains

[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:10: value === is not a member of slick.driver.MySQLDriver.simple.Column[C]
[error]         col(fields) === c
[error]                     ^
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:9: ambiguous implicit values:
[error]  both value BooleanColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Boolean]]
[error]  and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error]  match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error]       q.filter { fields =>
[error]                ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed Mar 6, 2014 1:21:48 AM

There have been questions about this, but the answers did not work for 2.0

How to parametrize Scala Slick queries by WHERE clause conditions?

like image 980
jilen Avatar asked Mar 05 '14 17:03

jilen


1 Answers

Slick doesn't have any information about C, so it doesn't know if it can and how it should map it to a database value and if it can use === on it. So you get a type error. You will have to use Scala's type system to restrict the type to one for which Slick knows how to map it. You can do this by providing a so-called Context Bound, in this case :BaseColumnType.

def whereEq[C:BaseColumnType](col: RichTable.this.type => Column[C], c: C) = {
  q.filter { fields =>
    col(fields) === c
  }
}

BaseColumnType is provided by Slick and using it in this way basically tells the Scala compiler to look for an implicit value of type BaseColumnType[C] in scope, where you call whereEq. Because then it is usually known what C will actually be. Slick comes with BaseColumnType[Int], BaseColumnType[String], etc. so at the call site, the Scala compiler can find one when your C is really an Int or String in that particular call and this way pass the info further to Slick.

Same for LiuTiger's question. abstract class Crud[..., PK:BaseColumnType] should do the trick, a trait doesn't work with context bounds. When implementing an abstract DAO be prepared to face a lot of challenges and get to the edges of your Scala type system skills and learn quite a bit about type inference order, implicit parameters, etc.

like image 85
cvogt Avatar answered Sep 20 '22 02:09

cvogt