Подтвердить что ты не робот

Пользовательское сопоставление для вложенной структуры класса case в Slick (более 22 столбцов)

Я пытаюсь сопоставить строку DB с более чем 22 столбцами в дереве классов case. Я бы предпочел не использовать HList, поскольку я не хочу работать с этим API, а также для некоторых экспоненциальных комментариев времени компиляции, которые я где-то читал...

Я прочитал эту тему, на которую ответил Stefan Zeiger: Как я могу обработать > 22 столбец с Slick с использованием вложенных кортежей или HLists?

Я видел этот тест, который показывает, как определить пользовательскую функцию сопоставления, и я хотел бы сделать это:

https://github.com/slick/slick/blob/2.1/slick-testkit/src/main/scala/com/typesafe/slick/testkit/tests/JdbcMapperTest.scala#L129-141

def * = (
        id,
        (p1i1, p1i2, p1i3, p1i4, p1i5, p1i6),
        (p2i1, p2i2, p2i3, p2i4, p2i5, p2i6),
        (p3i1, p3i2, p3i3, p3i4, p3i5, p3i6),
        (p4i1, p4i2, p4i3, p4i4, p4i5, p4i6)
      ).shaped <> ({ case (id, p1, p2, p3, p4) =>
        // We could do this without .shaped but then we'd have to write a type annotation for the parameters
        Whole(id, Part.tupled.apply(p1), Part.tupled.apply(p2), Part.tupled.apply(p3), Part.tupled.apply(p4))
      }, { w: Whole =>
        def f(p: Part) = Part.unapply(p).get
        Some((w.id, f(w.p1), f(w.p2), f(w.p3), f(w.p4)))
      })

Проблема в том, что я не могу это сделать!

Я попытался выполнить меньшие шаги.

class UserTable(tag: Tag) extends TableWithId[User](tag,"USER") {
  override def id = column[String]("id", O.PrimaryKey)
  def role = column[UserRole.Value]("role", O.NotNull)
  def login = column[String]("login", O.NotNull)
  def password = column[String]("password", O.NotNull)
  def firstName = column[String]("first_name", O.NotNull)
  def lastName = column[String]("last_name", O.NotNull)
  //
  def * = (id, role, login, password, firstName, lastName) <> (User.tupled,User.unapply)
  //
  def login_index = index("idx_user_login", login, unique = true)
}

Кажется, когда я звоню

(id, (firstName, lastName)).shaped

Тип ShapedValue[(Column[String], (Column[String], Column[String])), Nothing]

В то время как этот, кажется, работает нормально

(id, firstName, lastName).shaped

Параметр типа U не Nothing, но как ожидалось (String, String, String)

Я не понимаю, как работают все внутренние компоненты Slick. Может кто-нибудь объяснить мне, почему я не могу заставить мой код работать? Есть недостающий импорт или что-то еще?

Я думаю, мне нужно получить значение типа

ShapedValue[(Column[String], (Column[String], Column[String])), (String, (String, String))]

но я не знаю, почему он возвращает меня Nothing и не понимает, откуда взялись эти неявные параметры Shape...

Я хочу только, чтобы легко разделить мой столбец на 2 класса case

Спасибо

4b9b3361

Ответ 1

Кроме того, имеют ту же проблему с ограничением в 22 столбца, тестовый пример очень помогает. Не уверен, почему пример кода не работает для вас, следующий код прекрасно работает для меня,

case class UserRole(var role: String, var extra: String)
case class UserInfo(var login: String, var password: String, var firstName: String, var lastName: String)

case class User(id: Option[String], var info: UserInfo, var role: UserRole)

class UserTable(tag: Tag) extends Table[User](tag, "USER") {

  def id = column[String]("id", O.PrimaryKey)
  def role = column[String]("role", O.NotNull)
  def extra = column[String]("extra", O.NotNull)
  def login = column[String]("login", O.NotNull)
  def password = column[String]("password", O.NotNull)
  def firstName = column[String]("first_name", O.NotNull)
  def lastName = column[String]("last_name", O.NotNull)

  /** Projection */
  def * = (
    id,
    (login, password, firstName, lastName),
    (role, extra)
  ).shaped <> (

  { case (id, userInfo, userRole) =>
    User(Option[id], UserInfo.tupled.apply(userInfo), UserRole.tupled.apply(userRole))
  },
  { u: User =>
      def f1(p: UserInfo) = UserInfo.unapply(p).get
      def f2(p: UserRole) = UserRole.unapply(p).get
      Some((u.id.get, f1(u.info), f2(u.role)))
  })

  def login_index = index("id_user_login", login, unique = true)
}

Ответ 2

Как сказал Исонгрен, он работает отлично, но может быть сложно написать такой код, который раздражает работу с очень длинными кортежами... Я решил сделать это "шаг за шагом" и всегда предоставлял типы явно, чтобы избежать вывода типа проблемы, и теперь он отлично работает.

case class Patient(
                    id: String = java.util.UUID.randomUUID().toString,
                    companyScopeId: String,
                    assignedToUserId: Option[String] = None,
                    info: PatientInfo
                    ) extends ModelWithId



case class PatientInfo(
                        firstName: Option[String] = None,
                        lastName: Option[String] = None,
                        gender: Option[Gender.Value] = None,
                        alias: Option[String] = None,
                        street: Option[String] = None,
                        city: Option[String] = None,
                        postalCode: Option[String] = None,
                        phone: Option[String] = None,
                        mobilePhone: Option[String] = None,
                        email: Option[String] = None,
                        age: Option[AgeRange.Value] = None,
                        companySeniority: Option[CompanySeniorityRange.Value] = None,
                        employmentContract: Option[EmploymentContract.Value] = None,
                        socialStatus: Option[SocialStatus.Value] = None,
                        jobTitle: Option[String] = None
                        )

class PatientTable(tag: Tag) extends TableWithId[Patient](tag,"PATIENT") {
  override def id = column[String]("id", O.PrimaryKey)
  def companyScopeId = column[String]("company_scope_id", O.NotNull)
  def assignedToUserId = column[Option[String]]("assigned_to_user_id", O.Nullable)

  def firstName = column[Option[String]]("first_name", O.Nullable)
  def lastName = column[Option[String]]("last_name", O.Nullable)
  def gender = column[Option[Gender.Value]]("gender", O.Nullable)
  def alias = column[Option[String]]("alias", O.Nullable)
  def street = column[Option[String]]("street", O.Nullable)
  def city = column[Option[String]]("city", O.Nullable)
  def postalCode = column[Option[String]]("postal_code", O.Nullable)
  def phone = column[Option[String]]("phone", O.Nullable)
  def mobilePhone = column[Option[String]]("mobile_phone", O.Nullable)
  def email = column[Option[String]]("email", O.Nullable)
  def age = column[Option[AgeRange.Value]]("age", O.Nullable)
  def companySeniority = column[Option[CompanySeniorityRange.Value]]("company_seniority", O.Nullable)
  def employmentContract = column[Option[EmploymentContract.Value]]("employment_contract", O.Nullable)
  def socialStatus = column[Option[SocialStatus.Value]]("social_status", O.Nullable)
  def jobTitle = column[Option[String]]("job_title", O.Nullable)
  def role = column[Option[String]]("role", O.Nullable)



  private type PatientInfoTupleType = (Option[String], Option[String], Option[Gender.Value], Option[String], Option[String], Option[String], Option[String], Option[String], Option[String], Option[String], Option[AgeRange.Value], Option[CompanySeniorityRange.Value], Option[EmploymentContract.Value], Option[SocialStatus.Value], Option[String])
  private type PatientTupleType = (String, String, Option[String], PatientInfoTupleType)
  //
  private val patientShapedValue = (id, companyScopeId, assignedToUserId,
    (
      firstName, lastName, gender, alias, street, city, postalCode,
      phone, mobilePhone,email, age, companySeniority, employmentContract, socialStatus, jobTitle
      )
    ).shaped[PatientTupleType]
  //
  private val toModel: PatientTupleType => Patient = { patientTuple =>
    Patient(
      id = patientTuple._1,
      companyScopeId = patientTuple._2,
      assignedToUserId = patientTuple._3,
      info = PatientInfo.tupled.apply(patientTuple._4)
    )
  }
  private val toTuple: Patient => Option[PatientTupleType] = { patient =>
    Some {
      (
        patient.id,
        patient.companyScopeId,
        patient.assignedToUserId,
        (PatientInfo.unapply(patient.info).get)
        )
    }
  }

  def * = patientShapedValue <> (toModel,toTuple)
}

Ответ 3

Также вы можете использовать этот способ

class UserTable(tag: Tag) extends TableWithId[User](tag,"USER") {
  def id = column[String]("id", O.PrimaryKey)
  def role = column[String]("role", O.NotNull)
  def extra = column[String]("extra", O.NotNull)
  def login = column[String]("login", O.NotNull)
  def password = column[String]("password", O.NotNull)
  def firstName = column[String]("first_name", O.NotNull)
  def lastName = column[String]("last_name", O.NotNull)

  def loginMap = (login, password, firstName, lastName) <> (UserInfo.tupled, UserInfo.unapply)

  def roleMap = (role, extra) <> (Role.tupled, Role.unapply)

  override def * = (id, roleMap, loginMap) <> (User.tupled,User.unapply)

  def login_index = index("idx_user_login", login, unique = true)
}