2014-07-04 16 views
5

我正在使用Scala的'Slick和PostgreSQL。 而且我在配單PK的表上工作得很好。 現在我需要使用一個表有多個的PK:斯卡拉與多個PK insertOrUpdate()拋出異常錯誤:輸入結束時的語法錯誤

case class Report(f1: DateTime, 
    f2: String, 
    f3: Double) 

class Reports(tag: Tag) extends Table[Report](tag, "Reports") { 
    def f1 = column[DateTime]("f1") 
    def f2 = column[String]("f2") 
    def f3 = column[Double]("f3") 

    def * = (f1, f2, f3) <> (Report.tupled, Report.unapply) 
    def pk = primaryKey("pk_report", (f1, f2)) 
} 

val reports = TableQuery[Reports] 

時,我有空表,並使用reports.insert(report)它工作得很好。 但是當我使用reports.insertOrUpdate(report)我接受和例外:

Exception in thread "main" org.postgresql.util.PSQLException: ERROR: syntax error at end of input 
    Position: 76 
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102) 
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835) 
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257) 
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500) 
    at .... 

我在做什麼錯?如何解決它?

在此先感謝。


PS。我試圖解決辦法 - 試圖通過實施「是否存在更新其他插入」邏輯:

val len = reports.withFilter(_.f1 === report.f1).withFilter(_.f2 === report.f2).length.run.toInt 
        if(len == 1) { 
         println("Update: " + report) 
         reports.update(report) 
        } else { 
         println("Insert: " + report) 
         reports.insert(report) 
        } 

但我仍然得到更新異常:

Exception in thread "main" org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "pk_report" 
    Detail: Key ("f1", f2)=(2014-01-31 04:00:00, addon_io.aha.connect) already exists. 
+0

什麼是'insertOrUpdate',我沒有在'Slick' api中看到這樣的方法。 –

+0

我擁有它,當表有一個PK時,它可以很好地工作。我還能如何實現UPSERT操作? –

+1

Ende Neu:在Slick 2.1-M2中添加insertOrUpdate,並將成爲Slick 2.1的一部分。 – cvogt

回答

2

,你必須

val len = reports.withFilter(_.f1 === report.f1).withFilter(_.f2 === report.f2).length.run.toInt 
       if(len == 1) { 
        println("Update: " + report) 
        reports.update(report) 
       } else { 
        println("Insert: " + report) 
        reports.insert(report) 
       } 
第二部分

更改reports.update(report)

reports.filter(_.id === report.id).update(report)

其實你可以做一個filter電話與複合鍵在油滑的(至少PGSQL)損壞(更換你的第一個withFilter)在桌子上

7

關於你最初的問題,insertOrUpdate,所以誤差不上你的身邊。見bug報告如這裏:https://github.com/slick/slick/issues/966

所以你要設計一個解決辦法,然而「更新插入」操作很容易出現競爭情況,並且很難恰當地設計爲PostgreSQL沒有提供原生的功能來執行此。例如,請參閱http://www.depesz.com/2012/06/10/why-is-upsert-so-complicated/

無論如何,執行不太容易出現競爭條件的操作的另一種方法是首先更新(如果該行不存在,則不執行任何操作),然後執行「插入選擇「查詢,只有當行不存在時纔會插入。這是wàySlick將用單個PK對PostgreSQL執行insertOrUpdate操作。但是,「插入選擇」不能直接使用Slick來完成,您必須回退以指揮SQL。

0

我已經成功應用的技術描述here 所以我UPSERT方法是這樣的:

def upsert(model: String, module: String, timestamp: Long) = { 
    // see this article http://www.the-art-of-web.com/sql/upsert/ 
    val insert  = s"INSERT INTO $ModulesAffectedTableName (model, affected_module, timestamp) SELECT '$model','$module','$timestamp'" 
    val upsert  = s"UPDATE $ModulesAffectedTableName SET timestamp=$timestamp WHERE model='$model' AND affected_module='$module'" 
    val finalStmnt = s"WITH upsert AS ($upsert RETURNING *) $insert WHERE NOT EXISTS (SELECT * FROM upsert)" 
    conn.run(sqlu"#$finalStmnt") 
    } 
0

希望這個問題將在3.2.0

目前,我解決此問題通過創建用於創建表格的虛擬表格:

class ReportsDummy(tag: Tag) extends Table[Report](tag, "Reports") { 
    def f1 = column[DateTime]("f1") 
    def f2 = column[String]("f2") 
    def f3 = column[Double]("f3") 

    def * = (f1, f2, f3) <> (Report.tupled, Report.unapply) 
    def pk = primaryKey("pk_report", (f1, f2)) 
} 

和upsert的「真實」表

class Reports(tag: Tag) extends Table[Report](tag, "Reports") { 
    def f1 = column[DateTime]("f1", O.PrimaryKey) 
    def f2 = column[String]("f2", O.PrimaryKey) //two primary keys here, which would throw errors on table creation. Hence a dummy one for the task 
    def f3 = column[Double]("f3") 

    def * = (f1, f2, f3) <> (Report.tupled, Report.unapply) 
}