Slick 3.1 - Получение подмножества столбцов как класса case

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

Рассмотрим следующее определение таблицы:

class AuditResultTable(tag: Tag) extends Table[AuditResult](tag, AuditResultTableName) {
    def auditResultId: Rep[Long] = column[Long]("AuditResultId", O.PrimaryKey, O.AutoInc)
    def processorId: Rep[Long] = column[Long]("ProcessorId")
    def dispatchedTimestamp: Rep[Timestamp] = column[Timestamp]("DispatchedTimestamp", O.SqlType("timestamp(2)"))
    def SystemAOutput: Rep[Array[Byte]] = column[Array[Byte]]("SystemAOutput", O.SqlType("LONGBLOB"))
    def SystemBOutput: Rep[Array[Byte]]  = column[Array[Byte]]("SystemBOutput", O.SqlType("LONGBLOB"))
    def isSuccessful: Rep[Boolean] = column[Boolean]("IsSuccessful")


def * : ProvenShape[AuditResult] = (processorId, dispatchedTimestamp, systemAOutput, systemBOutput, isSuccessful, auditResultId) <>
  (AuditResult.tupled, AuditResult.unapply) 

}  

val auditResults = TableQuery[AuditResultTable]  

Соответствующий класс case:

case class AuditResult (
   ProcessorId: Long,
   DispatchedTimestamp: Timestamp,
   SystemAOutput: Array[Byte],
   SystemBOutput: Array[Byte],
   IsSuccessful: Boolean,
   AuditResultId: Long = 0L
 )

И, наконец, запрос доступа к данным:

def getRecentFailedAuditsQuery(): Query[AuditResultTable, AuditResult, Seq] = {
  auditResults.filterNot(r => r.isSuccessful)
}

Я рассмотрел и рассмотрел представленные варианты в этом (устаревшем) ответе и других:

  • Имея разную проекцию, чем проекция по умолчанию, которая сопоставляется с "легкой версией AuditResult, например AuditResultLight, которая опускает эти столбцы, несмотря на мои лучшие усилия, я не мог сделать эту работу - я чувствую, что это должно быть правильным подходом - как только у меня была" рабочая "проекция, у меня все еще была ошибка Slick" Нет соответствующей формы. Слик не знает, как сопоставить данные типы "
  • Построение иерархии классов с абстрактным классом AuditResultTableBase и двумя вытекающими из него классами - тем, который добавляет "тяжелые" столбцы, а один без них, как с их соответствующими проекциями по умолчанию, так и с классами. Это работает хорошо, но подход кажется неправильным и требует относительно большого изменения кода для такой легкой вещи.
  • Материализация кортежей вместо классов классов - это, конечно, будет работать, но я хочу, чтобы уровень доступа к данным был строго типизирован.

Какова идиоматическая/лучшая практика для Slick 3.1 для этой проблемы? Могу ли я использовать пользовательскую проекцию для этого, и если да, то что бы это выглядело для этого конкретного примера/запроса с SystemAOutput и SystemBOutput, являющимися тяжелыми столбцами, которые я хочу пропустить?

Ответы

Ответ 1

У меня была аналогичная проблема! Вы должны определить форму! С помощью документации мне удалось сделать этот подход с использованием "легкого" класса case.

Сначала определите более простой класс:

case class AuditResultLight(
  ProcessorId: Long,
  DispatchedTimestamp: Timestamp,
  IsSuccessful: Boolean,
  AuditResultId: Long = 0L
)

Затем вам нужно создать отмененную версию класса case:

case class AuditResultLightLifted(
  ProcessorId: Rep[Long],
  DispatchedTimestamp: Rep[Timestamp],
  IsSuccessful: Rep[Boolean],
  AuditResultId: Rep[Long]
)

Кроме того, вам нужен неявный объект (Shape), чтобы рассказать о том, как сопоставить один в другом:

implicit object AuditResultLightShape 
  extends CaseClassShape(AuditResultLightLifted.tupled, AuditResultLight.tupled)

Теперь вы можете определить запрос, который возвращает AuditResultLight (не точно проецирование, но насколько я понимаю, он работает аналогично):

val auditResultsLight = auditResults.map(r => AuditResultLightLifted(r.ProcessorId, r.DispatchedTimestamp, r.IsSuccessful, r.AuditResultId))  

Затем вы можете определить функцию, которая возвращает неудачные аудиты в легкой форме:

def getRecentFailedAuditsQuery(): Query[AuditResultTable, AuditResultLight, Seq] = {
  auditResultsLight.filterNot(r => r.isSuccessful)
}

Суть с кодом: https://gist.github.com/wjur/93712a51d392d181ab7fc2408e4ce48b

Код компилируется и выполняется, но в моем случае проблема заключается в том, что моя IDE (IntelliJ) сообщает Query[Nothing, Nothing, scala.Seq] для auditResultsLight. Я получаю синтаксические ошибки всякий раз, когда я использую auditResultsLight и ссылаюсь на поле AuditResultLight в запросе. Однако из-за этого, в конце концов, я решил использовать второй предложенный вами подход (тот, который содержит абстрактную таблицу). Почти такой же код, но с поддержкой IDE.