Как вернуть полную строку с помощью insertOrUpdate Slick

В настоящее время я изучаю Play2, Scala и Slick 3.1, и я довольно сильно застрял в синтаксисе использования insertOrUpdate и задаюсь вопросом, может ли кто-нибудь помочь мне.

Что я хочу сделать, так это вернуть полную строку при использовании insertOrUpdate, включая первичный ключ auto inc, но мне удалось вернуть только количество обновленных/вставленных строк.

Вот мое определение таблицы:

package models

final case class Report(session_id: Option[Long], session_name: String, tester_name: String, date: String, jira_ref: String,
                  duration: String, environment: String, notes: Option[String])

trait ReportDBTableDefinitions {

  import slick.driver.PostgresDriver.api._

  class Reports(tag: Tag) extends Table[Report](tag, "REPORTS") {

    def session_id = column[Long]("SESSION_ID", O.PrimaryKey, O.AutoInc)
    def session_name = column[String]("SESSION_NAME")
    def tester_name = column[String]("TESTER_NAME")
    def date = column[String]("DATE")
    def jira_ref = column[String]("JIRA_REF")
    def duration = column[String]("DURATION")
    def environment = column[String]("ENVIRONMENT")
    def notes = column[Option[String]]("NOTES")

    def * = (session_id.?, session_name, tester_name, date, jira_ref, duration, environment, notes) <> (Report.tupled, Report.unapply)
  }

  lazy val reportsTable = TableQuery[Reports]

}

Вот раздел моего DAO, относящийся к insertOrUpdate, и он отлично работает, но возвращает только количество обновленных/вставленных строк:

package models

import com.google.inject.Inject
import play.api.db.slick.DatabaseConfigProvider
import scala.concurrent.Future

class ReportsDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends DAOSlick {

  import driver.api._

  def save_report(report: Report): Future[Int] = {
    dbConfig.db.run(reportsTable.insertOrUpdate(report).transactionally)
  }
}

Я пробовал играть с «возвратом», но я не могу получить нужный мне синтаксис и продолжаю получать несоответствия типов, например. приведенное ниже не компилируется (потому что это, вероятно, совершенно неправильно!)

 def save_report(report: Report): Future[Report] = {
    dbConfig.db.run(reportsTable.returning(reportsTable).insertOrUpdate(report))
  }

Любая помощь приветствуется - я новичок в Scala и Slick, поэтому извиняюсь, если я упустил что-то действительно очевидное.


person jnhamilton    schedule 29.08.2016    source источник


Ответы (3)


Решено - опубликуйте его, если это поможет любому, кто пытается сделать что-то подобное:

//will return the new session_id on insert, and None on update
  def save_report(report: Report): Future[Option[Long]] = {
    val insertQuery = (reportsTable returning reportsTable.map(_.session_id)).insertOrUpdate(report)
    dbConfig.db.run(insertQuery)
  }

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

person jnhamilton    schedule 30.08.2016

Вы можете вернуть полную строку, но это опция, как указано в документации, она будет пустой при обновлении и будет Some(...), представляющей вставленную строку при вставке.

Таким образом, правильный код будет

    def save_report(report: Report): Future[Option[Report]] = {dbConfig.db.run(reportsTable.returning(reportsTable).insertOrUpdate(report))}
person Tony Truand    schedule 23.11.2017

Вы не можете вернуть весь отчет, сначала верните идентификатор (returning(reportsTable.map(_.session_id))), а затем получите весь объект

Проверьте, существует ли отчет в базе данных, если он существует, обновите его, если нет, продолжайте вставлять отчет в базу данных.

Обратите внимание, что вышеперечисленные операции можно выполнять полностью или без них с помощью транзакций.

def getReportDBIO(id: Long): DBIO[Report] = reportsTable.filter(_.session_id === id).result.head

def save_report(report: Report): Future[Report] = {
  val query = reportsTable.filter(_.session_id === report.session_id)
  val existsAction = query.exists.result
  val insertOrUpdateAction = 
  (for { 
     exists <- existsAction
     result <- exists match {
        case true => 
        query.update(report).flatMap {_ => getReportDBIO(report.session_id)}.transactionally
        case false => {
           val insertAction = reportsTable.returning(reportsTable.map(_.session_id)) += report
           val finalAction = insertAction.flatMap( id => getReportDBIO(id)).transactionally //transactionally is important
            finalAction
        }
     }
  } yield result).transactionally

  dbConfig.db.run(insertOrUpdateAction)
}

Соответствующим образом обновите функцию insertOrUpdate

person pamu    schedule 29.08.2016
comment
Большое спасибо за это - однако insertorupdate - это встроенная функция Slick, поэтому я не могу ее обновить как таковую. Я надеялся использовать его, потому что он либо обновляет запись, либо вставляет ее, если она не существует, но я не могу найти способ вернуть с ее помощью всю строку или даже идентификатор сеанса. - person jnhamilton; 29.08.2016
comment
@jnhamilton, в этом случае вы можете использовать save_report вместо insertOrUpdate. - person pamu; 29.08.2016
comment
Ах, я вижу, спасибо, я попробую это завтра и обновлю тикет - person jnhamilton; 29.08.2016
comment
Попробовал это, спасибо - он фактически вставляет новую запись, когда вы пытаетесь обновить, а не обновляете существующую запись, но это дало мне несколько идей, чтобы попробовать :) - person jnhamilton; 30.08.2016