【问题标题】:How to create slick projection for list of nested case class?如何为嵌套案例类列表创建光滑投影?
【发布时间】:2017-11-12 23:19:32
【问题描述】:

我正在使用 play 2.6.6scala 2.12.3slick 3.0.0

我最初有以下案例类结构,其中有一个嵌套案例类:

case class Device(id: Int, deviceUser: Option[DeviceUser] =None)

case class DeviceUser(name: Option[String] = None)

所以,我为设备类创建了以下投影:

class DevicesTable(tag: Tag) extends Table[Device](tag, "DEVICES") {

    def id = column[Int]("ID", O.PrimaryKey)

    def name = column[Option[String]]("NAME")

    def deviceUser = name.<>[Option[DeviceUser]](
      {
        (param: Option[String]) => {
          param match {
            case Some(name) => Some(DeviceUser(Some(name)))
            case None => None
          }
        }
      },
      {
        (t: Option[DeviceUser]) =>
         {
           t match {
             case Some(user) => Some(user.name)
             case None => None
           }
         }
      }
    )

    def * = (id, deviceUser).<>(Device.tupled, Device.unapply)
  }

上述设置工作正常。我可以使用上述投影轻松存储和检索数据。 But now, my requirement has changed and I need to store list of nested case class。所以,类结构现在如下:

case class Device(id: Int, deviceUser: Option[List[DeviceUser]] =None)

case class DeviceUser(name: Option[String] = None)

有没有什么方法可以为 deviceUser: Option[List[DeviceUser]] 字段定义投影?

更新:我在这里寻找更多non-relational 方法。

【问题讨论】:

  • 一个设备可以包含多个 DeviceUser - 因此您应该将其表示为数据库级别的一对多关系
  • 是的,这将是一种关系方式。但是,我在这里寻找更多 non-relational 的方法。
  • 将其序列化为 JSON/XML 并将其保存在 BLOB 列中。您的 DAL 应该解析它并创建所需的类型。
  • 这是一个很酷的想法,但我仍然希望数据在数据库中有意义
  • 但是为了在数据库级别上有意义,它不应该是关系的吗?也许您可以使用 NoSQL,例如 MongoDB,但据我所知,Slick 中不支持 MongoDB。如果您需要 Slick 的一对多关系示例,我可以提供。

标签: scala slick


【解决方案1】:

因为到目前为止,没有人提出解决方案,所以我分享我现在正在使用的方法。它有效,但当然不是最好的解决方案。特别是,我想避免在这里使用Await,并想开发一个generic implicit parser

另外,我必须创建一个单独的DeviceUsersTable

    case class DeviceUser(id: Int,name: Option[String] = None)

    class DeviceUserRepo @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)  {

      val dbConfig = dbConfigProvider.get[JdbcProfile]
      val db = dbConfig.db
      import dbConfig.profile.api._
      val DeviceUsers = TableQuery[DeviceUserTable]

      private def _findById(id: Int): DBIO[Option[DeviceUser]] =
        DeviceUsers.filter(_.id === id).result.headOption

      def findById(id: Int): Future[Option[DeviceUser]] =
        db.run(_findById(id))

      def all: Future[List[DeviceUser]] =
        db.run(DeviceUsers.to[List].result)

      def create(deviceUser: DeviceUser): Future[Int] = {
        db.run(DeviceUsers returning DeviceUsers.map(_.id) += deviceUser)
      }

      class DeviceUserTable(tag: Tag) extends Table[DeviceUser](tag, "DEVICE_USERS") {

        def id = column[Int]("ID", O.PrimaryKey)
        def name = column[Option[String]]("NAME")
        def * = (id, name).<>(DeviceUser.tupled, DeviceUser.unapply)
      }

    }

原来的DevicesTable 现在看起来像这样:

class DevicesTable(tag: Tag) extends Table[Device](tag, "DEVICES") {

    implicit val deviceUserConverter = MappedColumnType.base[Option[List[DeviceUser]], String](
      deviceUsersOpt => {
        deviceUsersOpt match {
          case Some(users:List[DeviceUser]) =>val listOfId = users.map{
            k => val res = deviceUserRepo.create(k)
              Await.result(res, 10 seconds)
          }
            listOfId.mkString(",")
          case None => ""
        }
      },
      str =>{
        val listOfIds = (str split "," map Integer.parseInt).toList.filterNot(k => k.equals(""))
        if(listOfIds.nonEmpty){
          val users = listOfIds.map{ k =>
            val res = deviceUserRepo.findById(k)
            Await.result(res, 10 seconds)
          }
          Some(users.flatten)
        } else {
          None
        }
      }
    )

    def id = column[Int]("ID", O.PrimaryKey)

    def deviceUser = column[Option[List[DeviceUser]]]("DEVICE_USERS")

    def * = (id, deviceUser).<>(Device.tupled, Device.unapply)
  }

【讨论】:

    猜你喜欢
    • 2014-08-19
    • 1970-01-01
    • 2019-11-08
    • 1970-01-01
    • 2018-08-03
    • 2013-11-08
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    相关资源
    最近更新 更多