多个光滑`列`用于相同的DB列中断投影

时间:2016-01-10 18:05:32

标签: scala slick slick-3.0

我是Slick的新手,因此我不确定错误使用implicits或Slick引起的问题是否不允许我做我想做的事情。

简而言之,我在Postgres中使用Slick-pg扩展名来支持JSONB。我还使用spray-json将JSONB字段反序列化为案例类。

为了自动将列转换为对象,我编写了通用隐式JsonColumnType,您可以在下面看到。它允许我有任何case类,我将json formatter定义为转换为jsonb字段。

另一方面,我希望同一列的别名为JsValue,以便我可以使用JSONB-operators

import com.github.tminglei.slickpg._
import com.github.tminglei.slickpg.json.PgJsonExtensions
import org.bson.types.ObjectId
import slick.ast.BaseTypedType
import slick.jdbc.JdbcType
import spray.json.{JsValue, RootJsonWriter, RootJsonReader}
import scala.reflect.ClassTag

trait MyPostgresDriver extends ExPostgresDriver with PgArraySupport with PgDate2Support with PgRangeSupport with PgHStoreSupport with PgSprayJsonSupport with PgJsonExtensions with PgSearchSupport with PgNetSupport with PgLTreeSupport {

  override def pgjson = "jsonb" // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json"
  override val api = MyAPI
  private val plainAPI = new API with SprayJsonPlainImplicits

  object MyAPI extends API with DateTimeImplicits with JsonImplicits with NetImplicits with LTreeImplicits with RangeImplicits with HStoreImplicits with SearchImplicits with SearchAssistants { //with ArrayImplicits

    implicit val ObjectIdColumnType = MappedColumnType.base[ObjectId, Array[Byte]](
      { obj => obj.toByteArray }, { arr => new ObjectId(arr) }
    )

    implicit def JsonColumnType[T: ClassTag](implicit reader: RootJsonReader[T], writer: RootJsonWriter[T]) = {
      val columnType: JdbcType[T] with BaseTypedType[T] = MappedColumnType.base[T, JsValue]({ obj => writer.write(obj) }, { json => reader.read(json) })
      columnType
    }
  }
}

object MyPostgresDriver extends MyPostgresDriver

以下是我的表格定义方式(最小化版本)

case class Article(id : ObjectId, ids : Ids)
case class Ids(doi: Option[String], pmid: Option[Long])

class ArticleRow(tag: Tag) extends Table[Article](tag, "articles") {

  def id = column[ObjectId]("id", O.PrimaryKey)
  def idsJson = column[JsValue]("ext_ids")
  def ids = column[Ids]("ext_ids")

  private val fromTuple: ((ObjectId, Ids)) => Article = {
    case (id, ids) => Article(id, ids)
  }
  private val toTuple = (v: Article) => Option((v.id, v.ids))

  def * = ProvenShape.proveShapeOf((id, ids) <> (fromTuple, toTuple))(MappedProjection.mappedProjectionShape)
}

private val articles = TableQuery[ArticleRow]

最后,我有按json字段值查找文章的功能

def getArticleByDoi(doi : String): Future[Article] = {
  val query = (for (a <- articles if (a.idsJson +>> "doi").asColumnOf[String] === doi) yield a).take(1).result
  slickDb.run(query).map { items =>
    items.headOption.getOrElse(throw new RuntimeException(s"Article with doi $doi is not found"))
  }
}

可悲的是,我在运行时

中遇到异常
java.lang.ClassCastException: spray.json.JsObject cannot be cast to server.models.db.Ids

问题发生在SpecializedJdbcResultConverter.base,其中ti.getValue被错误调用ti。它应该是slick.driver.JdbcTypesComponent$MappedJdbcType,而是它com.github.tminglei.slickpg.utils.PgCommonJdbcTypes$GenericJdbcType。结果错误的类型被传递到我的元组转换器。

是什么让Slick为列选择不同的类型,即使在表行类中有明确的投影定义?

演示该问题的示例项目是here

0 个答案:

没有答案