21

Slick マップされたテーブルで AutoInc キーを使用して PostgreSQL にレコードを挿入するにはどうすればよいですか? ケース クラスの id に Option を使用し、それを None に設定すると、PostgreSQL は挿入時にフィールドを null にすることはできないと文句を言います。これは H2 では機能しますが、PostgreSQL では機能しません。

//import scala.slick.driver.H2Driver.simple._
//import scala.slick.driver.BasicProfile.SimpleQL.Table
import scala.slick.driver.PostgresDriver.simple._
import Database.threadLocalSession

object TestMappedTable extends App{

    case class User(id: Option[Int], first: String, last: String)

    object Users extends Table[User]("users") {
        def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
        def first = column[String]("first")
        def last = column[String]("last")
        def * = id.? ~ first ~ last <> (User, User.unapply _)
        def ins1 = first ~ last returning id
        val findByID = createFinderBy(_.id)
        def autoInc = id.? ~ first ~ last <> (User, User.unapply _) returning id
    }

 // implicit val session = Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver").createSession()
    implicit val session = Database.forURL("jdbc:postgresql:test:slicktest",
                           driver="org.postgresql.Driver",
                           user="postgres",
                           password="xxx")

  session.withTransaction{
    Users.ddl.create

    // insert data
    print(Users.insert(User(None, "Jack", "Green" )))
    print(Users.insert(User(None, "Joe", "Blue" )))
    print(Users.insert(User(None, "John", "Purple" )))
    val u = Users.insert(User(None, "Jim", "Yellow" ))
  //  println(u.id.get)
    print(Users.autoInc.insert(User(None, "Johnathan", "Seagul" )))
  }
  session.withTransaction{
    val queryUsers = for {
    user <- Users
  } yield (user.id, user.first)
  println(queryUsers.list)

  Users.where(_.id between(1, 2)).foreach(println)
  println("ID 3 -> " + Users.findByID.first(3))
  }
}

上記を H2 で使用すると成功しますが、コメントアウトして PostgreSQL に変更すると、次のようになります。

[error] (run-main) org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
4

7 に答える 7

15

これはここで働いています:

object Application extends Table[(Long, String)]("application") {   
    def idlApplication = column[Long]("idlapplication", O.PrimaryKey, O.AutoInc)
    def appName = column[String]("appname")
    def * = idlApplication ~ appName
    def autoInc = appName returning idlApplication
}

var id = Application.autoInc.insert("App1")

これは私のSQLがどのように見えるかです:

CREATE TABLE application
(idlapplication BIGSERIAL PRIMARY KEY,
appName VARCHAR(500));

アップデート:

User でマップされたテーブルに関する特定の問題 (質問のように) は、次のように解決できます。

  def forInsert = first ~ last <>
    ({ (f, l) => User(None, f, l) }, { u:User => Some((u.first, u.last)) })

これは、Slick git リポジトリのテスト ケースからのものです。

于 2012-11-04T03:35:34.603 に答える
11

私は別の方法でこの問題に取り組みました。アプリケーション ロジックではオブジェクトが常に ID を持っていることを期待Userしており、ID を持たない唯一のポイントはデータベースへの挿入時であるためNewUser、ID を持たない補助ケース クラスを使用します。

case class User(id: Int, first: String, last: String)
case class NewUser(first: String, last: String)

object Users extends Table[User]("users") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def first = column[String]("first")
  def last = column[String]("last")

  def * = id ~ first ~ last <> (User, User.unapply _)
  def autoInc = first ~ last <> (NewUser, NewUser.unapply _) returning id
}

val id = Users.autoInc.insert(NewUser("John", "Doe"))

繰り返しますが、 User1:1 をデータベース エントリ/行にマップしますが、呼び出しNewUserのデータ コンテナーとしてのみ使用されるため、余分なケース クラスを持たないようにする場合は、タプルに置き換えることができます。insert

編集: より安全性が必要な場合 (やや冗長性を高めて) は、次のようにケース クラスの特性を利用できます。

trait UserT {
  def first: String
  def last: String
}
case class User(id: Int, first: String, last: String) extends UserT
case class NewUser(first: String, last: String) extends UserT
// ... the rest remains intact

この場合、モデルの変更を最初に (必要なミックスインを含めて) トレイトに適用し、オプションでデフォルト値をNewUser.

著者の意見: よりコンパクトであり、モデルへの変更は、ケース クラス宣言とテーブルの両方で、Userparamsをコピーして貼り付けてからid(auto-inc 主キー) を削除することであるため、私はまだ特徴のないソリューションを好みます。予測。

于 2013-03-17T00:21:07.353 に答える
2

少し異なるアプローチを使用しています。さらに射影を作成する代わりに、テーブルの次の ID を要求し、それをケース クラスにコピーして、テーブル エントリの挿入にデフォルトの射影 '*' を使用します。

postgres の場合、次のようになります。

Table-Object にこの特性を実装させます

trait TableWithId { this: Table[_] =>
  /**
   * can be overriden if the plural of tablename is irregular
   **/
  val idColName: String = s"${tableName.dropRight(1)}_id"
  def id = column[Int](s"${idColName}", O.PrimaryKey, O.AutoInc)
  def getNextId = (Q[Int] + s"""select nextval('"${tableName}_${idColName}_seq"')""").first
  }

すべてのエンティティ ケース クラスには、次のようなメソッドが必要です (トレイトでも定義する必要があります)。

case class Entity (...) {
  def withId(newId: Id): Entity = this.copy(id = Some(newId)
}

新しいエンティティを次の方法で挿入できるようになりました。

object Entities extends Table[Entity]("entities") with TableWithId {
  override val idColName: String = "entity_id"
  ...
  def save(entity: Entity) = this insert entity.withId(getNextId) 
}

テーブルごとに withId メソッドを定義する必要があるため、コードはまだ DRY ではありません。さらに、エンティティを挿入する前に次の ID を要求する必要がありますが、これはパフォーマンスに影響を与える可能性がありますが、一度に何千ものエントリを挿入しない限り注目すべきではありません。

主な利点は、特に多くの列を持つテーブルの場合に、コードのエラーが発生しにくくなる 2 番目のプロジェクションが必要ないことです。

于 2013-09-05T08:58:58.500 に答える
1

データベースを Postgres に変更したときに、play-slick-3.0 からコンピューター データベースのサンプルを作成しようとしたときに、同じ問題に直面しました。問題を解決したのは、進化ファイル /conf/evolutions/default/1.sql で id 列 (主キー) のタイプを SERIAL に変更することでした (元は BIGINT にありました)。 ディスカッション全体については、https ://groups.google.com/forum/?fromgroups=#%21topic/scalaquery/OEOF8HNzn2Uをご覧ください。
乾杯、ReneX

于 2015-09-27T04:44:46.143 に答える
0

私が見つけた解決策はSqlType("Serial")、列定義で使用することです。まだ広範囲にテストしていませんが、これまでのところうまくいくようです。

だから代わりに

def id: Rep[PK[SomeTable]] = column[PK[SomeTable]]("id", O.PrimaryKey, O.AutoInc)

やったほうがいい:

def id: Rep[PK[SomeTable]] = column[PK[SomeTable]]("id", SqlType("SERIAL"), O.PrimaryKey, O.AutoInc)

PK「Essential Slick」本の例のように定義されている場所:

final case class PK[A](value: Long = 0L) extends AnyVal with MappedTo[Long]
于 2019-07-06T01:20:20.423 に答える