В приложении есть следующие типы:
case class Widget(
id: Int,
name: String,
latlon: Option[Latlon],
)
case class Latlon(latitude: Double, longitude: Double)
Я хочу хранить виджеты в таблице со столбцами id
, name
, latitude
и longitude
(последние два являются необязательными).Мне все равно, что происходит, когда только один из столбцов латлона имеет значение ПУСТО (NULL), а другой - нет.
(В некоторых базах данных есть специальные типы столбцов для хранения географических координат. Пожалуйста, игнорируйте это для целей вопроса, поскольку типы были упрощены.)
Я попытался объявить таблицу следующим образом:
class Widgets(tag: Tag) extends Table[Widget](tag, Some(mySchema), "widgets") {
def id: Rep[Int] = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name: Rep[String] = column[String]("name")
def latitude: Rep[Option[Double]] = column[Option[Double]]("latitude")
def longitude: Rep[Option[Double]] = column[Option[Double]]("longitude")
def toLatlon(value: (Option[Double], Option[Double])): Option[Latlon] =
Applicative[Option].map2(value._1, value._2)(Latlon.apply)
def fromLatlon(value: Option[Latlon]): Option[(Option[Double], Option[Double])] =
value.map(latlon => (Some(latlon.latitude), Some(latlon.longitude)))
def * =
(
id.?,
name,
alternateNames,
(latitude, longitude) <> (toLatlon, fromLatlon),
) <> (Widget.apply _ tupled, Widget.unapply)
}
Это работает для извлечения данных, но при вставке данных без a latlon
, возникает ошибка:
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:366)
at scala.None$.get(Option.scala:364)
at slick.lifted.ShapedValue.$anonfun$$less$greater$1(Shape.scala:279)
at scala.Function1.$anonfun$andThen$1(Function1.scala:57)
at slick.relational.TypeMappingResultConverter.set(ResultConverter.scala:135)
at slick.relational.ProductResultConverter.set(ResultConverter.scala:68)
at slick.relational.ProductResultConverter.set(ResultConverter.scala:43)
at slick.relational.TypeMappingResultConverter.set(ResultConverter.scala:135)
at slick.jdbc.JdbcActionComponent$InsertActionComposerImpl$SingleInsertAction.$anonfun$run$15(JdbcActionComponent.scala:521)
at slick.jdbc.JdbcBackend$SessionDef.withPreparedInsertStatement(JdbcBackend.scala:432)
at slick.jdbc.JdbcBackend$SessionDef.withPreparedInsertStatement$(JdbcBackend.scala:429)
at slick.jdbc.JdbcBackend$BaseSession.withPreparedInsertStatement(JdbcBackend.scala:489)
at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.preparedInsert(JdbcActionComponent.scala:662)
at slick.jdbc.JdbcActionComponent$InsertActionComposerImpl$SingleInsertAction.run(JdbcActionComponent.scala:519)
at slick.jdbc.JdbcActionComponent$SimpleJdbcProfileAction.run(JdbcActionComponent.scala:30)
at slick.jdbc.JdbcActionComponent$SimpleJdbcProfileAction.run(JdbcActionComponent.scala:27)
at slick.basic.BasicBackend$DatabaseDef$$anon$3.liftedTree1$1(BasicBackend.scala:275)
at slick.basic.BasicBackend$DatabaseDef$$anon$3.run(BasicBackend.scala:275)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
(дополнительные Option
в fromLatlon
есть, потому что, очевидно, тип <>
требует этого.)
Я предпринял еще одну попытку, используя Slick документацию для отображения пользовательских классов :
case class LiftedLatlon(latitude: Rep[Double], longitude: Rep[Double])
implicit object LatlonShape extends CaseClassShape(LiftedLatlon.tupled, Latlon.apply _ tupled)
def * =
(
id.?,
name,
alternateNames,
LiftedLatlon(latitude, longitude),
) <> (Widget.apply _ tupled, Widget.unapply)
Кажется, что это сработало бы для необходимого столбца, но это типы latitude
, longitude
и первый аргумент <>
не совпадают, потому что в классе Widget
latlon
является необязательным.
Как сгруппировать два необязательных поля, которые у меня есть, в одно и иметь возможностьвставить все значение без дополнительной части?
Почему в аргументах <>
(f: (U => R), g: (R => Option[U]
) существует асимметрия?