Ошибка Scala Anorm Postgresql при сохранении массива байтов

У меня есть таблица базы данных в Scala Playframework, определенная как

CREATE TABLE account (
    id     SERIAL,
    email  TEXT  NOT NULL,
    buffer BYTEA NOT NULL,
    PRIMARY KEY (id)
);

Я использую буфер протокола для сериализации объекта в массив байтов со следующим кодом

DB.withConnection{ implicit c=>
  SQL("INSERT INTO device (buffer,secret) VALUES ({secret},{buffer})").on(
    "secret"->device.getSecret(),
    "buffer"->device.toByteArray()
  ).executeInsert()
}

device.toByteArray() возвращает тип Array[Byte], который должен соответствовать типу базы данных для этого столбца. Однако при выполнении кода я получаю

play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[PSQLException: ERROR: column "buffer" is of type bytea but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 44]]
    at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:134) [play_2.9.1.jar:2.0.3]
    at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:115) [play_2.9.1.jar:2.0.3]
    at akka.actor.Actor$class.apply(Actor.scala:318) [akka-actor.jar:2.0.2]
    at play.core.ActionInvoker.apply(Invoker.scala:113) [play_2.9.1.jar:2.0.3]
    at akka.actor.ActorCell.invoke(ActorCell.scala:626) [akka-actor.jar:2.0.2]
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:197) [akka-actor.jar:2.0.2]
Caused by: org.postgresql.util.PSQLException: ERROR: column "buffer" is of type bytea but expression is of type character varying

person Jacob Groundwater    schedule 17.08.2012    source источник


Ответы (1)


Глядя на anorm и документы Postgres, похоже, вы нужно будет добавить обработчик для правильного хранения Array[Byte], используя setBytes вместо setObject, что требует текущий код.

Я бы расширил соответствие в любом параметре в рамках, чтобы прочитать

    value match {
      case Some(bd: java.math.BigDecimal) => stmt.setBigDecimal(index, bd)
      case Some(b: Array[Byte]) => stmt.setBytes(index, b)
      case Some(o) => stmt.setObject(index, o)
      // ...

и добавить

implicit val byteArrayToStatement = new ToStatement[Array[Byte]] {
  def set(s: java.sql.PreparedStatement, index: Int, aValue: Array[Byte]): Unit = setAny(index, aValue, s)
}

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

person themel    schedule 17.08.2012
comment
Вау, это потрясающий ответ. Я заметил, что если я жестко кодирую secret и only передаю параметр Array[Byte], он преуспевает. Это похоже на ошибку, и ваш комментарий как бы подкрепляет эту идею. - person Jacob Groundwater; 17.08.2012